Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsGlobalWindowOuter.cpp
blobc678a0a94150e73c6f5cd086c91a9848d4d0cfb8
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 "nsGlobalWindowOuter.h"
10 #include "nsGlobalWindowInner.h"
12 #include <algorithm>
14 #include "mozilla/MemoryReporting.h"
16 // Local Includes
17 #include "Navigator.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/dom/AutoPrintEventDispatcher.h"
28 #include "mozilla/dom/BindingUtils.h"
29 #include "mozilla/dom/BrowserChild.h"
30 #include "mozilla/dom/BrowsingContextBinding.h"
31 #include "mozilla/dom/CanonicalBrowsingContext.h"
32 #include "mozilla/dom/ContentChild.h"
33 #include "mozilla/dom/ContentFrameMessageManager.h"
34 #include "mozilla/dom/DocumentInlines.h"
35 #include "mozilla/dom/EventTarget.h"
36 #include "mozilla/dom/HTMLIFrameElement.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/ProxyHandlerUtils.h"
43 #include "mozilla/dom/StorageEvent.h"
44 #include "mozilla/dom/StorageEventBinding.h"
45 #include "mozilla/dom/StorageNotifierService.h"
46 #include "mozilla/dom/StorageUtils.h"
47 #include "mozilla/dom/Timeout.h"
48 #include "mozilla/dom/TimeoutHandler.h"
49 #include "mozilla/dom/TimeoutManager.h"
50 #include "mozilla/dom/UserActivation.h"
51 #include "mozilla/dom/WindowContext.h"
52 #include "mozilla/dom/WindowFeatures.h" // WindowFeatures
53 #include "mozilla/dom/WindowProxyHolder.h"
54 #include "mozilla/IntegerPrintfMacros.h"
55 #include "mozilla/StorageAccessAPIHelper.h"
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/CallAndConstruct.h" // JS::Call
83 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
84 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxy
85 #include "js/PropertyAndElement.h" // JS_DefineObject, JS_GetProperty
86 #include "js/PropertySpec.h"
87 #include "js/RealmIterators.h"
88 #include "js/Wrapper.h"
89 #include "nsLayoutUtils.h"
90 #include "nsReadableUtils.h"
91 #include "nsJSEnvironment.h"
92 #include "mozilla/dom/ScriptSettings.h"
93 #include "mozilla/Preferences.h"
94 #include "mozilla/Likely.h"
95 #include "mozilla/SchedulerGroup.h"
96 #include "mozilla/SpinEventLoopUntil.h"
97 #include "mozilla/Sprintf.h"
98 #include "mozilla/Unused.h"
100 // Other Classes
101 #include "mozilla/dom/BarProps.h"
102 #include "nsLayoutStatics.h"
103 #include "nsCCUncollectableMarker.h"
104 #include "mozilla/dom/WorkerCommon.h"
105 #include "mozilla/dom/ToJSValue.h"
106 #include "nsJSPrincipals.h"
107 #include "mozilla/Attributes.h"
108 #include "mozilla/Components.h"
109 #include "mozilla/Debug.h"
110 #include "mozilla/EventListenerManager.h"
111 #include "mozilla/MouseEvents.h"
112 #include "mozilla/PresShell.h"
113 #include "mozilla/ProcessHangMonitor.h"
114 #include "mozilla/StaticPrefs_dom.h"
115 #include "mozilla/StaticPrefs_full_screen_api.h"
116 #include "mozilla/StaticPrefs_print.h"
117 #include "mozilla/StaticPrefs_fission.h"
118 #include "mozilla/ThrottledEventQueue.h"
119 #include "AudioChannelService.h"
120 #include "nsAboutProtocolUtils.h"
121 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
122 #include "PostMessageEvent.h"
123 #include "mozilla/dom/DocGroup.h"
124 #include "mozilla/net/CookieJarSettings.h"
126 // Interfaces Needed
127 #include "nsIFrame.h"
128 #include "nsCanvasFrame.h"
129 #include "nsIWidget.h"
130 #include "nsIWidgetListener.h"
131 #include "nsIBaseWindow.h"
132 #include "nsIDeviceSensors.h"
133 #include "nsIContent.h"
134 #include "nsIDocShell.h"
135 #include "mozilla/dom/Document.h"
136 #include "Crypto.h"
137 #include "nsDOMString.h"
138 #include "nsThreadUtils.h"
139 #include "nsILoadContext.h"
140 #include "nsIScrollableFrame.h"
141 #include "nsView.h"
142 #include "nsViewManager.h"
143 #include "nsIPrompt.h"
144 #include "nsIPromptService.h"
145 #include "nsIPromptFactory.h"
146 #include "nsIWritablePropertyBag2.h"
147 #include "nsIWebNavigation.h"
148 #include "nsIWebBrowserChrome.h"
149 #include "nsIWebBrowserFind.h" // For window.find()
150 #include "nsComputedDOMStyle.h"
151 #include "nsDOMCID.h"
152 #include "nsDOMWindowUtils.h"
153 #include "nsIWindowWatcher.h"
154 #include "nsPIWindowWatcher.h"
155 #include "nsIDocumentViewer.h"
156 #include "nsIScriptError.h"
157 #include "nsISHistory.h"
158 #include "nsIControllers.h"
159 #include "nsGlobalWindowCommands.h"
160 #include "nsQueryObject.h"
161 #include "nsContentUtils.h"
162 #include "nsCSSProps.h"
163 #include "nsIURIFixup.h"
164 #include "nsIURIMutator.h"
165 #include "mozilla/EventDispatcher.h"
166 #include "mozilla/EventStateManager.h"
167 #include "nsIObserverService.h"
168 #include "nsFocusManager.h"
169 #include "nsIAppWindow.h"
170 #include "nsServiceManagerUtils.h"
171 #include "mozilla/dom/CustomEvent.h"
172 #include "nsIScreenManager.h"
173 #include "nsIClassifiedChannel.h"
174 #include "nsIXULRuntime.h"
175 #include "xpcprivate.h"
177 #ifdef NS_PRINTING
178 # include "nsIPrintSettings.h"
179 # include "nsIPrintSettingsService.h"
180 # include "nsIWebBrowserPrint.h"
181 #endif
183 #include "nsWindowRoot.h"
184 #include "nsNetCID.h"
185 #include "nsIArray.h"
187 #include "nsIDOMXULCommandDispatcher.h"
189 #include "mozilla/GlobalKeyListener.h"
191 #include "nsIDragService.h"
192 #include "mozilla/dom/Element.h"
193 #include "mozilla/dom/Selection.h"
194 #include "nsFrameLoader.h"
195 #include "nsFrameLoaderOwner.h"
196 #include "nsXPCOMCID.h"
197 #include "mozilla/Logging.h"
198 #include "mozilla/ProfilerMarkers.h"
199 #include "prenv.h"
201 #include "mozilla/dom/IDBFactory.h"
202 #include "mozilla/dom/MessageChannel.h"
203 #include "mozilla/dom/Promise.h"
205 #include "mozilla/dom/Gamepad.h"
206 #include "mozilla/dom/GamepadManager.h"
208 #include "gfxVR.h"
209 #include "VRShMem.h"
210 #include "FxRWindowManager.h"
211 #include "mozilla/dom/VRDisplay.h"
212 #include "mozilla/dom/VRDisplayEvent.h"
213 #include "mozilla/dom/VRDisplayEventBinding.h"
214 #include "mozilla/dom/VREventObserver.h"
216 #include "nsRefreshDriver.h"
218 #include "mozilla/extensions/WebExtensionPolicy.h"
220 #include "mozilla/BasePrincipal.h"
221 #include "mozilla/Services.h"
222 #include "mozilla/Telemetry.h"
223 #include "mozilla/dom/Location.h"
224 #include "nsHTMLDocument.h"
225 #include "nsWrapperCacheInlines.h"
226 #include "mozilla/DOMEventTargetHelper.h"
227 #include "prrng.h"
228 #include "nsSandboxFlags.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/NavigatorBinding.h"
246 #include "mozilla/dom/ImageBitmap.h"
247 #include "mozilla/dom/ImageBitmapBinding.h"
248 #include "mozilla/dom/ServiceWorkerRegistration.h"
249 #include "mozilla/dom/WebIDLGlobalNameHash.h"
250 #include "mozilla/dom/Worklet.h"
251 #include "AccessCheck.h"
253 #ifdef MOZ_WEBSPEECH
254 # include "mozilla/dom/SpeechSynthesis.h"
255 #endif
257 #ifdef ANDROID
258 # include <android/log.h>
259 #endif
261 #ifdef XP_WIN
262 # include <process.h>
263 # define getpid _getpid
264 #else
265 # include <unistd.h> // for getpid()
266 #endif
268 using namespace mozilla;
269 using namespace mozilla::dom;
270 using namespace mozilla::dom::ipc;
271 using mozilla::BasePrincipal;
272 using mozilla::OriginAttributes;
273 using mozilla::TimeStamp;
274 using mozilla::layout::RemotePrintJobChild;
276 static inline nsGlobalWindowInner* GetCurrentInnerWindowInternal(
277 const nsGlobalWindowOuter* aOuter) {
278 return nsGlobalWindowInner::Cast(aOuter->GetCurrentInnerWindow());
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(this)->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(this)->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(this)->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 using Base = MaybeCrossOriginObject<js::Wrapper>;
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(JS::GCContext* gcx, 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(JS::GCContext* gcx, 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);
582 bool nsOuterWindowProxy::getOwnPropertyDescriptor(
583 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
584 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const {
585 // First check for indexed access. This is
586 // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
587 // step 2, mostly.
588 JS::Rooted<JS::Value> subframe(cx);
589 bool found;
590 if (!GetSubframeWindow(cx, proxy, id, &subframe, found)) {
591 return false;
593 if (found) {
594 // Step 2.4.
596 desc.set(Some(JS::PropertyDescriptor::Data(
597 subframe, {
598 JS::PropertyAttribute::Configurable,
599 JS::PropertyAttribute::Enumerable,
600 })));
601 return true;
604 bool isSameOrigin = IsPlatformObjectSameOrigin(cx, proxy);
606 // If we did not find a subframe, we could still have an indexed property
607 // access. In that case we should throw a SecurityError in the cross-origin
608 // case.
609 if (!isSameOrigin && IsArrayIndex(GetArrayIndexFromId(id))) {
610 // Step 2.5.2.
611 return ReportCrossOriginDenial(cx, id, "access"_ns);
614 // Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an
615 // IsArrayIndex(GetArrayIndexFromId(id)) here. We'll never have a property on
616 // the Window whose name is an index, because our defineProperty doesn't pass
617 // those on to the Window.
619 // Step 3.
620 if (isSameOrigin) {
621 if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) {
622 Window_Binding::CountMaybeMissingProperty(proxy, id);
625 // Fall through to js::Wrapper.
626 { // Scope for JSAutoRealm while we are dealing with js::Wrapper.
627 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
628 // for now. That's what js::Wrapper expects, and since we're same-origin
629 // anyway this is not changing any security behavior.
630 JSAutoRealm ar(cx, proxy);
631 JS_MarkCrossZoneId(cx, id);
632 bool ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
633 if (!ok) {
634 return false;
637 #if 0
638 // See https://github.com/tc39/ecma262/issues/672 for more information.
639 if (desc.isSome() &&
640 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
641 (*desc).setConfigurable(true);
643 #endif
646 // Now wrap our descriptor back into the Realm that asked for it.
647 return JS_WrapPropertyDescriptor(cx, desc);
650 // Step 4.
651 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
652 return false;
655 // Step 5
656 if (desc.isSome()) {
657 return true;
660 // Non-spec step for the PDF viewer's window.print(). This comes before we
661 // check for named subframes, because in the same-origin case print() would
662 // shadow those.
663 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) {
664 if (!MaybeGetPDFJSPrintMethod(cx, proxy, desc)) {
665 return false;
668 if (desc.isSome()) {
669 return true;
673 // Step 6 -- check for named subframes.
674 if (id.isString()) {
675 nsAutoJSString name;
676 if (!name.init(cx, id.toString())) {
677 return false;
679 nsGlobalWindowOuter* win = GetOuterWindow(proxy);
680 if (RefPtr<BrowsingContext> childDOMWin = win->GetChildWindow(name)) {
681 JS::Rooted<JS::Value> childValue(cx);
682 if (!ToJSValue(cx, WindowProxyHolder(childDOMWin), &childValue)) {
683 return false;
685 desc.set(Some(JS::PropertyDescriptor::Data(
686 childValue, {JS::PropertyAttribute::Configurable})));
687 return true;
691 // And step 7.
692 return CrossOriginPropertyFallback(cx, proxy, id, desc);
695 bool nsOuterWindowProxy::definePropertySameOrigin(
696 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
697 JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const {
698 if (IsArrayIndex(GetArrayIndexFromId(id))) {
699 // Spec says to Reject whether this is a supported index or not,
700 // since we have no indexed setter or indexed creator. It is up
701 // to the caller to decide whether to throw a TypeError.
702 return result.failCantDefineWindowElement();
705 JS::ObjectOpResult ourResult;
706 bool ok = js::Wrapper::defineProperty(cx, proxy, id, desc, ourResult);
707 if (!ok) {
708 return false;
711 if (!ourResult.ok()) {
712 // It's possible that this failed because the page got the existing
713 // descriptor (which we force to claim to be configurable) and then tried to
714 // redefine the property with the descriptor it got but a different value.
715 // We want to allow this case to succeed, so check for it and if we're in
716 // that case try again but now with an attempt to define a non-configurable
717 // property.
718 if (!desc.hasConfigurable() || !desc.configurable()) {
719 // The incoming descriptor was not explicitly marked "configurable: true",
720 // so it failed for some other reason. Just propagate that reason out.
721 result = ourResult;
722 return true;
725 JS::Rooted<Maybe<JS::PropertyDescriptor>> existingDesc(cx);
726 ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, &existingDesc);
727 if (!ok) {
728 return false;
730 if (existingDesc.isNothing() || existingDesc->configurable()) {
731 // We have no existing property, or its descriptor is already configurable
732 // (on the Window itself, where things really can be non-configurable).
733 // So we failed for some other reason, which we should propagate out.
734 result = ourResult;
735 return true;
738 JS::Rooted<JS::PropertyDescriptor> updatedDesc(cx, desc);
739 updatedDesc.setConfigurable(false);
741 JS::ObjectOpResult ourNewResult;
742 ok = js::Wrapper::defineProperty(cx, proxy, id, updatedDesc, ourNewResult);
743 if (!ok) {
744 return false;
747 if (!ourNewResult.ok()) {
748 // Twiddling the configurable flag didn't help. Just return this failure
749 // out to the caller.
750 result = ourNewResult;
751 return true;
755 #if 0
756 // See https://github.com/tc39/ecma262/issues/672 for more information.
757 if (desc.hasConfigurable() && !desc.configurable() &&
758 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
759 // Give callers a way to detect that they failed to "really" define a
760 // non-configurable property.
761 result.failCantDefineWindowNonConfigurable();
762 return true;
764 #endif
766 result.succeed();
767 return true;
770 bool nsOuterWindowProxy::ownPropertyKeys(
771 JSContext* cx, JS::Handle<JSObject*> proxy,
772 JS::MutableHandleVector<jsid> props) const {
773 // Just our indexed stuff followed by our "normal" own property names.
774 if (!AppendIndexedPropertyNames(proxy, props)) {
775 return false;
778 if (IsPlatformObjectSameOrigin(cx, proxy)) {
779 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
780 // for now. That's what js::Wrapper expects, and since we're same-origin
781 // anyway this is not changing any security behavior.
782 JS::RootedVector<jsid> innerProps(cx);
783 { // Scope for JSAutoRealm so we can mark the ids once we exit it
784 JSAutoRealm ar(cx, proxy);
785 if (!js::Wrapper::ownPropertyKeys(cx, proxy, &innerProps)) {
786 return false;
789 for (auto& id : innerProps) {
790 JS_MarkCrossZoneId(cx, id);
792 return js::AppendUnique(cx, props, innerProps);
795 // In the cross-origin case we purposefully exclude subframe names from the
796 // list of property names we report here.
797 JS::Rooted<JSObject*> holder(cx);
798 if (!EnsureHolder(cx, proxy, &holder)) {
799 return false;
802 JS::RootedVector<jsid> crossOriginProps(cx);
803 if (!js::GetPropertyKeys(cx, holder,
804 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
805 &crossOriginProps) ||
806 !js::AppendUnique(cx, props, crossOriginProps)) {
807 return false;
810 // Add the "print" property if needed.
811 nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
812 nsGlobalWindowInner* inner =
813 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
814 if (inner) {
815 nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner);
816 if (targetPrincipal &&
817 nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) {
818 JS::RootedVector<jsid> printProp(cx);
819 if (!printProp.append(GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) ||
820 !js::AppendUnique(cx, props, printProp)) {
821 return false;
826 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
829 bool nsOuterWindowProxy::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
830 JS::Handle<jsid> id,
831 JS::ObjectOpResult& result) const {
832 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
833 return ReportCrossOriginDenial(cx, id, "delete"_ns);
836 if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
837 // Fail (which means throw if strict, else return false).
838 return result.failCantDeleteWindowElement();
841 if (IsArrayIndex(GetArrayIndexFromId(id))) {
842 // Indexed, but not supported. Spec says return true.
843 return result.succeed();
846 // We're same-origin, so it should be safe to enter the Realm of "proxy".
847 // Let's do that, just in case, to avoid cross-compartment issues in our
848 // js::Wrapper caller..
849 JSAutoRealm ar(cx, proxy);
850 JS_MarkCrossZoneId(cx, id);
851 return js::Wrapper::delete_(cx, proxy, id, result);
854 JSObject* nsOuterWindowProxy::getSameOriginPrototype(JSContext* cx) const {
855 return Window_Binding::GetProtoObjectHandle(cx);
858 bool nsOuterWindowProxy::has(JSContext* cx, JS::Handle<JSObject*> proxy,
859 JS::Handle<jsid> id, bool* bp) const {
860 // We could just directly forward this method to js::BaseProxyHandler, but
861 // that involves reifying the actual property descriptor, which might be more
862 // work than we have to do for has() on the Window.
864 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
865 // In the cross-origin case we only have own properties. Just call hasOwn
866 // directly.
867 return hasOwn(cx, proxy, id, bp);
870 if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
871 *bp = true;
872 return true;
875 // Just to be safe in terms of compartment asserts, enter the Realm of
876 // "proxy". We're same-origin with it, so this should be safe.
877 JSAutoRealm ar(cx, proxy);
878 JS_MarkCrossZoneId(cx, id);
879 return js::Wrapper::has(cx, proxy, id, bp);
882 bool nsOuterWindowProxy::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
883 JS::Handle<jsid> id, bool* bp) const {
884 // We could just directly forward this method to js::BaseProxyHandler, but
885 // that involves reifying the actual property descriptor, which might be more
886 // work than we have to do for hasOwn() on the Window.
888 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
889 // Avoiding reifying the property descriptor here would require duplicating
890 // a bunch of "is this property exposed cross-origin" logic, which is
891 // probably not worth it. Just forward this along to the base
892 // implementation.
894 // It's very important to not forward this to js::Wrapper, because that will
895 // not do the right security and cross-origin checks and will pass through
896 // the call to the Window.
898 // The BaseProxyHandler code is OK with this happening without entering the
899 // compartment of "proxy".
900 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
903 if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
904 *bp = true;
905 return true;
908 // Just to be safe in terms of compartment asserts, enter the Realm of
909 // "proxy". We're same-origin with it, so this should be safe.
910 JSAutoRealm ar(cx, proxy);
911 JS_MarkCrossZoneId(cx, id);
912 return js::Wrapper::hasOwn(cx, proxy, id, bp);
915 bool nsOuterWindowProxy::get(JSContext* cx, JS::Handle<JSObject*> proxy,
916 JS::Handle<JS::Value> receiver,
917 JS::Handle<jsid> id,
918 JS::MutableHandle<JS::Value> vp) const {
919 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
920 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
921 vp.set(JS::ObjectValue(*proxy));
922 return MaybeWrapValue(cx, vp);
925 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
926 return CrossOriginGet(cx, proxy, receiver, id, vp);
929 bool found;
930 if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
931 return false;
934 if (found) {
935 return true;
938 if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) {
939 Window_Binding::CountMaybeMissingProperty(proxy, id);
942 { // Scope for JSAutoRealm
943 // Enter "proxy"'s Realm. We're in the same-origin case, so this should be
944 // safe.
945 JSAutoRealm ar(cx, proxy);
947 JS_MarkCrossZoneId(cx, id);
949 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
950 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
951 return false;
954 // Fall through to js::Wrapper.
955 if (!js::Wrapper::get(cx, proxy, wrappedReceiver, id, vp)) {
956 return false;
960 // Make sure our return value is in the caller compartment.
961 return MaybeWrapValue(cx, vp);
964 bool nsOuterWindowProxy::set(JSContext* cx, JS::Handle<JSObject*> proxy,
965 JS::Handle<jsid> id, JS::Handle<JS::Value> v,
966 JS::Handle<JS::Value> receiver,
967 JS::ObjectOpResult& result) const {
968 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
969 return CrossOriginSet(cx, proxy, id, v, receiver, result);
972 if (IsArrayIndex(GetArrayIndexFromId(id))) {
973 // Reject the set. It's up to the caller to decide whether to throw a
974 // TypeError. If the caller is strict mode JS code, it'll throw.
975 return result.failReadOnly();
978 // Do the rest in the Realm of "proxy", since we're in the same-origin case.
979 JSAutoRealm ar(cx, proxy);
980 JS::Rooted<JS::Value> wrappedArg(cx, v);
981 if (!MaybeWrapValue(cx, &wrappedArg)) {
982 return false;
984 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
985 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
986 return false;
989 JS_MarkCrossZoneId(cx, id);
991 return js::Wrapper::set(cx, proxy, id, wrappedArg, wrappedReceiver, result);
994 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
995 JSContext* cx, JS::Handle<JSObject*> proxy,
996 JS::MutableHandleVector<jsid> props) const {
997 // We could just stop overring getOwnEnumerablePropertyKeys and let our
998 // superclasses deal (by falling back on the BaseProxyHandler implementation
999 // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to
1000 // only return the enumerable ones. But maybe there's value in having
1001 // somewhat faster for-in iteration on Window objects...
1003 // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable
1004 // own property names.
1005 if (!AppendIndexedPropertyNames(proxy, props)) {
1006 return false;
1009 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
1010 // All the cross-origin properties other than the indexed props are
1011 // non-enumerable, so we're done here.
1012 return true;
1015 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
1016 // for now. That's what js::Wrapper expects, and since we're same-origin
1017 // anyway this is not changing any security behavior.
1018 JS::RootedVector<jsid> innerProps(cx);
1019 { // Scope for JSAutoRealm so we can mark the ids once we exit it.
1020 JSAutoRealm ar(cx, proxy);
1021 if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx, proxy, &innerProps)) {
1022 return false;
1026 for (auto& id : innerProps) {
1027 JS_MarkCrossZoneId(cx, id);
1030 return js::AppendUnique(cx, props, innerProps);
1033 bool nsOuterWindowProxy::GetSubframeWindow(JSContext* cx,
1034 JS::Handle<JSObject*> proxy,
1035 JS::Handle<jsid> id,
1036 JS::MutableHandle<JS::Value> vp,
1037 bool& found) const {
1038 Nullable<WindowProxyHolder> frame = GetSubframeWindow(cx, proxy, id);
1039 if (frame.IsNull()) {
1040 found = false;
1041 return true;
1044 found = true;
1045 return WrapObject(cx, frame.Value(), vp);
1048 Nullable<WindowProxyHolder> nsOuterWindowProxy::GetSubframeWindow(
1049 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const {
1050 uint32_t index = GetArrayIndexFromId(id);
1051 if (!IsArrayIndex(index)) {
1052 return nullptr;
1055 nsGlobalWindowOuter* win = GetOuterWindow(proxy);
1056 return win->IndexedGetterOuter(index);
1059 bool nsOuterWindowProxy::AppendIndexedPropertyNames(
1060 JSObject* proxy, JS::MutableHandleVector<jsid> props) const {
1061 uint32_t length = GetOuterWindow(proxy)->Length();
1062 MOZ_ASSERT(int32_t(length) >= 0);
1063 if (!props.reserve(props.length() + length)) {
1064 return false;
1066 for (int32_t i = 0; i < int32_t(length); ++i) {
1067 if (!props.append(JS::PropertyKey::Int(i))) {
1068 return false;
1072 return true;
1075 bool nsOuterWindowProxy::EnsureHolder(
1076 JSContext* cx, JS::Handle<JSObject*> proxy,
1077 JS::MutableHandle<JSObject*> holder) const {
1078 return EnsureHolder(cx, proxy, HOLDER_WEAKMAP_SLOT,
1079 Window_Binding::sCrossOriginProperties, holder);
1082 size_t nsOuterWindowProxy::objectMoved(JSObject* obj, JSObject* old) const {
1083 nsGlobalWindowOuter* outerWindow = GetOuterWindow(obj);
1084 if (outerWindow) {
1085 outerWindow->UpdateWrapper(obj, old);
1086 BrowsingContext* bc = outerWindow->GetBrowsingContext();
1087 if (bc) {
1088 bc->UpdateWindowProxy(obj, old);
1091 return 0;
1094 enum { PDFJS_SLOT_CALLEE = 0 };
1096 // static
1097 bool nsOuterWindowProxy::MaybeGetPDFJSPrintMethod(
1098 JSContext* cx, JS::Handle<JSObject*> proxy,
1099 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) {
1100 MOZ_ASSERT(proxy);
1101 MOZ_ASSERT(!desc.isSome());
1103 nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
1104 nsGlobalWindowInner* inner =
1105 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
1106 if (!inner) {
1107 // No print method to expose.
1108 return true;
1111 nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner);
1112 if (!targetPrincipal) {
1113 // Nothing special to be done.
1114 return true;
1117 if (!nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) {
1118 // Not our origin's PDF document.
1119 return true;
1122 // Get the function we plan to actually call.
1123 JS::Rooted<JSObject*> innerObj(cx, inner->GetGlobalJSObject());
1124 if (!innerObj) {
1125 // Really should not happen, but ok, let's just return.
1126 return true;
1129 JS::Rooted<JS::Value> targetFunc(cx);
1131 JSAutoRealm ar(cx, innerObj);
1132 if (!JS_GetProperty(cx, innerObj, "print", &targetFunc)) {
1133 return false;
1137 if (!targetFunc.isObject()) {
1138 // Who knows what's going on. Just return.
1139 return true;
1142 // The Realm of cx is the realm our caller is in and the realm we
1143 // should create our function in. Note that we can't use the
1144 // standard XPConnect function forwarder machinery because our
1145 // "this" is cross-origin, so we have to do thus by hand.
1147 // Make sure targetFunc is wrapped into the right compartment.
1148 if (!MaybeWrapValue(cx, &targetFunc)) {
1149 return false;
1152 JSFunction* fun =
1153 js::NewFunctionWithReserved(cx, PDFJSPrintMethod, 0, 0, "print");
1154 if (!fun) {
1155 return false;
1158 JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
1159 js::SetFunctionNativeReserved(funObj, PDFJS_SLOT_CALLEE, targetFunc);
1161 // { value: <print>, writable: true, enumerable: true, configurable: true }
1162 // because that's what it would have been in the same-origin case without
1163 // the PDF viewer messing with things.
1164 desc.set(Some(JS::PropertyDescriptor::Data(
1165 JS::ObjectValue(*funObj),
1166 {JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
1167 JS::PropertyAttribute::Writable})));
1168 return true;
1171 // static
1172 bool nsOuterWindowProxy::PDFJSPrintMethod(JSContext* cx, unsigned argc,
1173 JS::Value* vp) {
1174 JS::CallArgs args = CallArgsFromVp(argc, vp);
1176 JS::Rooted<JSObject*> realCallee(
1177 cx, &js::GetFunctionNativeReserved(&args.callee(), PDFJS_SLOT_CALLEE)
1178 .toObject());
1179 // Unchecked unwrap, because we want to extract the thing we really had
1180 // before.
1181 realCallee = js::UncheckedUnwrap(realCallee);
1183 JS::Rooted<JS::Value> thisv(cx, args.thisv());
1184 if (thisv.isNullOrUndefined()) {
1185 // Replace it with the global of our stashed callee, simulating the
1186 // global-assuming behavior of DOM methods.
1187 JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(realCallee));
1188 if (!MaybeWrapObject(cx, &global)) {
1189 return false;
1191 thisv.setObject(*global);
1192 } else if (!thisv.isObject()) {
1193 return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
1196 // We want to do an UncheckedUnwrap here, because we're going to directly
1197 // examine the principal of the inner window, if we have an inner window.
1198 JS::Rooted<JSObject*> unwrappedObj(cx,
1199 js::UncheckedUnwrap(&thisv.toObject()));
1200 nsGlobalWindowInner* inner = nullptr;
1202 // Do the unwrap in the Realm of the object we're looking at.
1203 JSAutoRealm ar(cx, unwrappedObj);
1204 UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window, &unwrappedObj, inner, cx);
1206 if (!inner) {
1207 return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
1210 nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal(cx);
1211 if (!callerPrincipal->SubsumesConsideringDomain(inner->GetPrincipal())) {
1212 // Check whether it's a PDF viewer from our origin.
1213 nsCOMPtr<nsIPrincipal> pdfPrincipal = GetNoPDFJSPrincipal(inner);
1214 if (!pdfPrincipal || !callerPrincipal->Equals(pdfPrincipal)) {
1215 // Security error.
1216 return ThrowInvalidThis(cx, args, true, prototypes::id::Window);
1220 // Go ahead and enter the Realm of our real callee to call it. We'll pass it
1221 // our "thisv", just in case someone grabs a "print" method off one PDF
1222 // document and .call()s it on another one.
1224 JSAutoRealm ar(cx, realCallee);
1225 if (!MaybeWrapValue(cx, &thisv)) {
1226 return false;
1229 // Don't bother passing through the args; they will get ignored anyway.
1231 if (!JS::Call(cx, thisv, realCallee, JS::HandleValueArray::empty(),
1232 args.rval())) {
1233 return false;
1237 // Wrap the return value (not that there should be any!) into the right
1238 // compartment.
1239 return MaybeWrapValue(cx, args.rval());
1242 // static
1243 already_AddRefed<nsIPrincipal> nsOuterWindowProxy::GetNoPDFJSPrincipal(
1244 nsGlobalWindowInner* inner) {
1245 if (!nsContentUtils::IsPDFJS(inner->GetPrincipal())) {
1246 return nullptr;
1249 if (Document* doc = inner->GetExtantDoc()) {
1250 if (nsCOMPtr<nsIPropertyBag2> propBag =
1251 do_QueryInterface(doc->GetChannel())) {
1252 nsCOMPtr<nsIPrincipal> principal(
1253 do_GetProperty(propBag, u"noPDFJSPrincipal"_ns));
1254 return principal.forget();
1257 return nullptr;
1260 const nsOuterWindowProxy nsOuterWindowProxy::singleton;
1262 class nsChromeOuterWindowProxy : public nsOuterWindowProxy {
1263 public:
1264 constexpr nsChromeOuterWindowProxy() = default;
1266 const char* className(JSContext* cx,
1267 JS::Handle<JSObject*> wrapper) const override;
1269 static const nsChromeOuterWindowProxy singleton;
1272 const char* nsChromeOuterWindowProxy::className(
1273 JSContext* cx, JS::Handle<JSObject*> proxy) const {
1274 MOZ_ASSERT(js::IsProxy(proxy));
1276 return "ChromeWindow";
1279 const nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton;
1281 static JSObject* NewOuterWindowProxy(JSContext* cx,
1282 JS::Handle<JSObject*> global,
1283 bool isChrome) {
1284 MOZ_ASSERT(JS_IsGlobalObject(global));
1286 JSAutoRealm ar(cx, global);
1288 js::WrapperOptions options;
1289 options.setClass(&OuterWindowProxyClass);
1290 JSObject* obj =
1291 js::Wrapper::New(cx, global,
1292 isChrome ? &nsChromeOuterWindowProxy::singleton
1293 : &nsOuterWindowProxy::singleton,
1294 options);
1295 MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
1296 return obj;
1299 //*****************************************************************************
1300 //*** nsGlobalWindowOuter: Object Management
1301 //*****************************************************************************
1303 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID)
1304 : nsPIDOMWindowOuter(aWindowID),
1305 mFullscreenHasChangedDuringProcessing(false),
1306 mForceFullScreenInWidget(false),
1307 mIsClosed(false),
1308 mInClose(false),
1309 mHavePendingClose(false),
1310 mBlockScriptedClosingFlag(false),
1311 mWasOffline(false),
1312 mCreatingInnerWindow(false),
1313 mIsChrome(false),
1314 mAllowScriptsToClose(false),
1315 mTopLevelOuterContentWindow(false),
1316 mDelayedPrintUntilAfterLoad(false),
1317 mDelayedCloseForPrinting(false),
1318 mShouldDelayPrintUntilAfterLoad(false),
1319 #ifdef DEBUG
1320 mSerial(0),
1321 mSetOpenerWindowCalled(false),
1322 #endif
1323 mCleanedUp(false),
1324 mCanSkipCCGeneration(0),
1325 mAutoActivateVRDisplayID(0) {
1326 AssertIsOnMainThread();
1327 SetIsOnMainThread();
1328 nsLayoutStatics::AddRef();
1330 // Initialize the PRCList (this).
1331 PR_INIT_CLIST(this);
1333 // |this| is an outer window. Outer windows start out frozen and
1334 // remain frozen until they get an inner window.
1335 MOZ_ASSERT(IsFrozen());
1337 // We could have failed the first time through trying
1338 // to create the entropy collector, so we should
1339 // try to get one until we succeed.
1341 #ifdef DEBUG
1342 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
1344 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1345 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1346 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1347 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1348 nullptr));
1349 #endif
1351 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
1352 ("DOMWINDOW %p created outer=nullptr", this));
1354 // Add ourselves to the outer windows list.
1355 MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!");
1357 // |this| is an outer window, add to the outer windows list.
1358 MOZ_ASSERT(!sOuterWindowsById->Contains(mWindowID),
1359 "This window shouldn't be in the hash table yet!");
1360 // We seem to see crashes in release builds because of null
1361 // |sOuterWindowsById|.
1362 if (sOuterWindowsById) {
1363 sOuterWindowsById->InsertOrUpdate(mWindowID, this);
1367 #ifdef DEBUG
1369 /* static */
1370 void nsGlobalWindowOuter::AssertIsOnMainThread() {
1371 MOZ_ASSERT(NS_IsMainThread());
1374 #endif // DEBUG
1376 /* static */
1377 void nsGlobalWindowOuter::Init() {
1378 AssertIsOnMainThread();
1380 NS_ASSERTION(gDOMLeakPRLogOuter,
1381 "gDOMLeakPRLogOuter should have been initialized!");
1383 sOuterWindowsById = new OuterWindowByIdTable();
1386 nsGlobalWindowOuter::~nsGlobalWindowOuter() {
1387 AssertIsOnMainThread();
1389 if (sOuterWindowsById) {
1390 sOuterWindowsById->Remove(mWindowID);
1393 nsContentUtils::InnerOrOuterWindowDestroyed();
1395 #ifdef DEBUG
1396 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
1397 nsAutoCString url;
1398 if (mLastOpenedURI) {
1399 url = mLastOpenedURI->GetSpecOrDefault();
1401 // Data URLs can be very long, so truncate to avoid flooding the log.
1402 const uint32_t maxURLLength = 1000;
1403 if (url.Length() > maxURLLength) {
1404 url.Truncate(maxURLLength);
1408 MOZ_LOG(
1409 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
1410 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1411 "%s]\n",
1412 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1413 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1414 nullptr, url.get()));
1416 #endif
1418 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
1419 ("DOMWINDOW %p destroyed", this));
1421 JSObject* proxy = GetWrapperMaybeDead();
1422 if (proxy) {
1423 if (mBrowsingContext && mBrowsingContext->GetUnbarrieredWindowProxy()) {
1424 nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
1425 mBrowsingContext->GetUnbarrieredWindowProxy());
1426 // Check that the current WindowProxy object corresponds to this
1427 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
1428 // we've replaced it with a cross-process WindowProxy.
1429 if (outer == this) {
1430 mBrowsingContext->ClearWindowProxy();
1433 js::SetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT,
1434 JS::PrivateValue(nullptr));
1437 // An outer window is destroyed with inner windows still possibly
1438 // alive, iterate through the inner windows and null out their
1439 // back pointer to this outer, and pull them out of the list of
1440 // inner windows.
1442 // Our linked list of inner windows both contains (an nsGlobalWindowOuter),
1443 // and our inner windows (nsGlobalWindowInners). This means that we need to
1444 // use PRCList*. We can then compare that PRCList* to `this` to see if its an
1445 // inner or outer window.
1446 PRCList* w;
1447 while ((w = PR_LIST_HEAD(this)) != this) {
1448 PR_REMOVE_AND_INIT_LINK(w);
1451 DropOuterWindowDocs();
1453 // Outer windows are always supposed to call CleanUp before letting themselves
1454 // be destroyed.
1455 MOZ_ASSERT(mCleanedUp);
1457 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1458 if (ac) ac->RemoveWindowAsListener(this);
1460 nsLayoutStatics::Release();
1463 // static
1464 void nsGlobalWindowOuter::ShutDown() {
1465 AssertIsOnMainThread();
1467 delete sOuterWindowsById;
1468 sOuterWindowsById = nullptr;
1471 void nsGlobalWindowOuter::DropOuterWindowDocs() {
1472 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
1473 mDoc = nullptr;
1474 mSuspendedDocs.Clear();
1477 void nsGlobalWindowOuter::CleanUp() {
1478 // Guarantee idempotence.
1479 if (mCleanedUp) return;
1480 mCleanedUp = true;
1482 StartDying();
1484 mWindowUtils = nullptr;
1486 ClearControllers();
1488 mContext = nullptr; // Forces Release
1489 mChromeEventHandler = nullptr; // Forces Release
1490 mParentTarget = nullptr;
1491 mMessageManager = nullptr;
1493 mArguments = nullptr;
1496 void nsGlobalWindowOuter::ClearControllers() {
1497 if (mControllers) {
1498 uint32_t count;
1499 mControllers->GetControllerCount(&count);
1501 while (count--) {
1502 nsCOMPtr<nsIController> controller;
1503 mControllers->GetControllerAt(count, getter_AddRefs(controller));
1505 nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
1506 if (context) context->SetCommandContext(nullptr);
1509 mControllers = nullptr;
1513 //*****************************************************************************
1514 // nsGlobalWindowOuter::nsISupports
1515 //*****************************************************************************
1517 // QueryInterface implementation for nsGlobalWindowOuter
1518 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowOuter)
1519 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1520 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1521 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1522 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1523 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1524 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1525 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1526 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter)
1527 NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy)
1528 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1529 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1530 NS_INTERFACE_MAP_END
1532 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter)
1533 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter)
1535 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter)
1536 if (tmp->IsBlackForCC(false)) {
1537 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1538 return true;
1540 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1541 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1542 elm->MarkForCC();
1544 return true;
1546 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1548 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowOuter)
1549 return tmp->IsBlackForCC(true);
1550 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1552 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowOuter)
1553 return tmp->IsBlackForCC(false);
1554 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1556 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowOuter)
1558 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter)
1559 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1560 char name[512];
1561 nsAutoCString uri;
1562 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1563 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1565 SprintfLiteral(name, "nsGlobalWindowOuter # %" PRIu64 " outer %s",
1566 tmp->mWindowID, uri.get());
1567 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1568 } else {
1569 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowOuter, tmp->mRefCnt.get())
1572 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
1574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
1577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1578 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDocs)
1579 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1580 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal)
1581 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1582 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal)
1583 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1585 // Traverse stuff from nsPIDOMWindow
1586 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1587 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1588 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
1589 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
1591 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
1592 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1594 tmp->TraverseObjectsInGlobal(cb);
1596 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
1597 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1599 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter)
1600 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1601 if (sOuterWindowsById) {
1602 sOuterWindowsById->Remove(tmp->mWindowID);
1605 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
1607 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1608 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
1610 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1611 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDocs)
1612 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1613 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal)
1614 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1615 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal)
1616 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1618 // Unlink stuff from nsPIDOMWindow
1619 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1620 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1621 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
1622 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
1624 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
1625 if (tmp->mBrowsingContext) {
1626 if (tmp->mBrowsingContext->GetUnbarrieredWindowProxy()) {
1627 nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
1628 tmp->mBrowsingContext->GetUnbarrieredWindowProxy());
1629 // Check that the current WindowProxy object corresponds to this
1630 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
1631 // we've replaced it with a cross-process WindowProxy.
1632 if (outer == tmp) {
1633 tmp->mBrowsingContext->ClearWindowProxy();
1636 tmp->mBrowsingContext = nullptr;
1639 tmp->UnlinkObjectsInGlobal();
1641 if (tmp->IsChromeWindow()) {
1642 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
1645 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1646 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1648 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowOuter)
1649 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1650 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1652 bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded) {
1653 if (!nsCCUncollectableMarker::sGeneration) {
1654 return false;
1657 // Unlike most wrappers, the outer window wrapper is not a wrapper for
1658 // the outer window. Instead, the outer window wrapper holds the inner
1659 // window binding object, which in turn holds the nsGlobalWindowInner, which
1660 // has a strong reference to the nsGlobalWindowOuter. We're using the
1661 // mInnerWindow pointer as a flag for that whole chain.
1662 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1663 (mInnerWindow && HasKnownLiveWrapper())) &&
1664 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1667 //*****************************************************************************
1668 // nsGlobalWindowOuter::nsIScriptGlobalObject
1669 //*****************************************************************************
1671 bool nsGlobalWindowOuter::ShouldResistFingerprinting(RFPTarget aTarget) const {
1672 if (mDoc) {
1673 return mDoc->ShouldResistFingerprinting(aTarget);
1675 return nsContentUtils::ShouldResistFingerprinting(
1676 "If we do not have a document then we do not have any context"
1677 "to make an informed RFP choice, so we fall back to the global pref",
1678 aTarget);
1681 OriginTrials nsGlobalWindowOuter::Trials() const {
1682 return mInnerWindow ? nsGlobalWindowInner::Cast(mInnerWindow)->Trials()
1683 : OriginTrials();
1686 FontFaceSet* nsGlobalWindowOuter::GetFonts() {
1687 if (mDoc) {
1688 return mDoc->Fonts();
1690 return nullptr;
1693 nsresult nsGlobalWindowOuter::EnsureScriptEnvironment() {
1694 if (GetWrapperPreserveColor()) {
1695 return NS_OK;
1698 NS_ENSURE_STATE(!mCleanedUp);
1700 NS_ASSERTION(!GetCurrentInnerWindowInternal(this),
1701 "No cached wrapper, but we have an inner window?");
1702 NS_ASSERTION(!mContext, "Will overwrite mContext!");
1704 // If this window is an [i]frame, don't bother GC'ing when the frame's context
1705 // is destroyed since a GC will happen when the frameset or host document is
1706 // destroyed anyway.
1707 mContext = new nsJSContext(mBrowsingContext->IsTop(), this);
1708 return NS_OK;
1711 nsIScriptContext* nsGlobalWindowOuter::GetScriptContext() { return mContext; }
1713 bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* aNewDocument) {
1714 // We reuse the inner window when:
1715 // a. We are currently at our original document.
1716 // b. At least one of the following conditions are true:
1717 // -- The new document is the same as the old document. This means that we're
1718 // getting called from document.open().
1719 // -- The new document has the same origin as what we have loaded right now.
1721 if (!mDoc || !aNewDocument) {
1722 return false;
1725 if (!mDoc->IsInitialDocument()) {
1726 return false;
1729 #ifdef DEBUG
1731 nsCOMPtr<nsIURI> uri;
1732 NS_GetURIWithoutRef(mDoc->GetDocumentURI(), getter_AddRefs(uri));
1733 NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
1735 #endif
1737 // Great, we're the original document, check for one of the other
1738 // conditions.
1740 if (mDoc == aNewDocument) {
1741 return true;
1744 if (aNewDocument->IsStaticDocument()) {
1745 return false;
1748 if (BasePrincipal::Cast(mDoc->NodePrincipal())
1749 ->FastEqualsConsideringDomain(aNewDocument->NodePrincipal())) {
1750 // The origin is the same.
1751 return true;
1754 return false;
1757 void nsGlobalWindowOuter::SetInitialPrincipal(
1758 nsIPrincipal* aNewWindowPrincipal, nsIContentSecurityPolicy* aCSP,
1759 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP) {
1760 // We should never create windows with an expanded principal.
1761 // If we have a system principal, make sure we're not using it for a content
1762 // docshell.
1763 // NOTE: Please keep this logic in sync with
1764 // nsAppShellService::JustCreateTopWindow
1765 if (nsContentUtils::IsExpandedPrincipal(aNewWindowPrincipal) ||
1766 (aNewWindowPrincipal->IsSystemPrincipal() &&
1767 GetBrowsingContext()->IsContent())) {
1768 aNewWindowPrincipal = nullptr;
1771 // If there's an existing document, bail if it either:
1772 if (mDoc) {
1773 // (a) is not an initial about:blank document, or
1774 if (!mDoc->IsInitialDocument()) return;
1775 // (b) already has the correct principal.
1776 if (mDoc->NodePrincipal() == aNewWindowPrincipal) return;
1778 #ifdef DEBUG
1779 // If we have a document loaded at this point, it had better be about:blank.
1780 // Otherwise, something is really weird. An about:blank page has a
1781 // NullPrincipal.
1782 bool isNullPrincipal;
1783 MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(
1784 &isNullPrincipal)) &&
1785 isNullPrincipal);
1786 #endif
1789 // Use the subject (or system) principal as the storage principal too until
1790 // the new window finishes navigating and gets a real storage principal.
1791 nsDocShell::Cast(GetDocShell())
1792 ->CreateAboutBlankDocumentViewer(aNewWindowPrincipal, aNewWindowPrincipal,
1793 aCSP, nullptr,
1794 /* aIsInitialDocument */ true, aCOEP);
1796 if (mDoc) {
1797 MOZ_ASSERT(mDoc->IsInitialDocument(),
1798 "document should be initial document");
1801 RefPtr<PresShell> presShell = GetDocShell()->GetPresShell();
1802 if (presShell && !presShell->DidInitialize()) {
1803 // Ensure that if someone plays with this document they will get
1804 // layout happening.
1805 presShell->Initialize();
1809 #define WINDOWSTATEHOLDER_IID \
1811 0x0b917c3e, 0xbd50, 0x4683, { \
1812 0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26 \
1816 class WindowStateHolder final : public nsISupports {
1817 public:
1818 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
1819 NS_DECL_ISUPPORTS
1821 explicit WindowStateHolder(nsGlobalWindowInner* aWindow);
1823 nsGlobalWindowInner* GetInnerWindow() { return mInnerWindow; }
1825 void DidRestoreWindow() {
1826 mInnerWindow = nullptr;
1827 mInnerWindowReflector = nullptr;
1830 protected:
1831 ~WindowStateHolder();
1833 nsGlobalWindowInner* mInnerWindow;
1834 // We hold onto this to make sure the inner window doesn't go away. The outer
1835 // window ends up recalculating it anyway.
1836 JS::PersistentRooted<JSObject*> mInnerWindowReflector;
1839 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
1841 WindowStateHolder::WindowStateHolder(nsGlobalWindowInner* aWindow)
1842 : mInnerWindow(aWindow),
1843 mInnerWindowReflector(RootingCx(), aWindow->GetWrapper()) {
1844 MOZ_ASSERT(aWindow, "null window");
1846 aWindow->Suspend();
1848 // When a global goes into the bfcache, we disable script.
1849 xpc::Scriptability::Get(mInnerWindowReflector).SetWindowAllowsScript(false);
1852 WindowStateHolder::~WindowStateHolder() {
1853 if (mInnerWindow) {
1854 // This window was left in the bfcache and is now going away. We need to
1855 // free it up.
1856 // Note that FreeInnerObjects may already have been called on the
1857 // inner window if its outer has already had SetDocShell(null)
1858 // called.
1859 mInnerWindow->FreeInnerObjects();
1863 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
1865 bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument,
1866 SecureContextFlags aFlags) {
1867 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
1868 if (principal->IsSystemPrincipal()) {
1869 return true;
1872 // Implement https://w3c.github.io/webappsec-secure-contexts/#settings-object
1873 // With some modifications to allow for aFlags.
1875 bool hadNonSecureContextCreator = false;
1877 if (WindowContext* parentWindow =
1878 GetBrowsingContext()->GetParentWindowContext()) {
1879 hadNonSecureContextCreator = !parentWindow->GetIsSecureContext();
1882 if (hadNonSecureContextCreator) {
1883 return false;
1886 if (nsContentUtils::HttpsStateIsModern(aDocument)) {
1887 return true;
1890 if (principal->GetIsNullPrincipal()) {
1891 // If the NullPrincipal has a valid precursor URI we want to use it to
1892 // construct the principal otherwise we fall back to the original document
1893 // URI.
1894 nsCOMPtr<nsIPrincipal> precursorPrin = principal->GetPrecursorPrincipal();
1895 nsCOMPtr<nsIURI> uri = precursorPrin ? precursorPrin->GetURI() : nullptr;
1896 if (!uri) {
1897 uri = aDocument->GetOriginalURI();
1899 // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so
1900 // it doesn't actually matter what we use here, but reusing the document
1901 // principal's attributes is convenient.
1902 const OriginAttributes& attrs = principal->OriginAttributesRef();
1903 // CreateContentPrincipal correctly gets a useful principal for blob: and
1904 // other URI_INHERITS_SECURITY_CONTEXT URIs.
1905 principal = BasePrincipal::CreateContentPrincipal(uri, attrs);
1906 if (NS_WARN_IF(!principal)) {
1907 return false;
1911 return principal->GetIsOriginPotentiallyTrustworthy();
1914 static bool InitializeLegacyNetscapeObject(JSContext* aCx,
1915 JS::Handle<JSObject*> aGlobal) {
1916 JSAutoRealm ar(aCx, aGlobal);
1918 // Note: MathJax depends on window.netscape being exposed. See bug 791526.
1919 JS::Rooted<JSObject*> obj(aCx);
1920 obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr);
1921 NS_ENSURE_TRUE(obj, false);
1923 obj = JS_DefineObject(aCx, obj, "security", nullptr);
1924 NS_ENSURE_TRUE(obj, false);
1926 return true;
1929 struct MOZ_STACK_CLASS CompartmentFinderState {
1930 explicit CompartmentFinderState(nsIPrincipal* aPrincipal)
1931 : principal(aPrincipal), compartment(nullptr) {}
1933 // Input: we look for a compartment which is same-origin with the
1934 // given principal.
1935 nsIPrincipal* principal;
1937 // Output: We set this member if we find a compartment.
1938 JS::Compartment* compartment;
1941 static JS::CompartmentIterResult FindSameOriginCompartment(
1942 JSContext* aCx, void* aData, JS::Compartment* aCompartment) {
1943 auto* data = static_cast<CompartmentFinderState*>(aData);
1944 MOZ_ASSERT(!data->compartment, "Why are we getting called?");
1946 // If this compartment is not safe to share across globals, don't do
1947 // anything with it; in particular we should not be getting a
1948 // CompartmentPrivate from such a compartment, because it may be in
1949 // the middle of being collected and its CompartmentPrivate may no
1950 // longer be valid.
1951 if (!js::IsSharableCompartment(aCompartment)) {
1952 return JS::CompartmentIterResult::KeepGoing;
1955 auto* compartmentPrivate = xpc::CompartmentPrivate::Get(aCompartment);
1956 if (!compartmentPrivate->CanShareCompartmentWith(data->principal)) {
1957 // Can't reuse this one, keep going.
1958 return JS::CompartmentIterResult::KeepGoing;
1961 // We have a winner!
1962 data->compartment = aCompartment;
1963 return JS::CompartmentIterResult::Stop;
1966 static JS::RealmCreationOptions& SelectZone(
1967 JSContext* aCx, nsIPrincipal* aPrincipal, nsGlobalWindowInner* aNewInner,
1968 JS::RealmCreationOptions& aOptions) {
1969 // Use the shared system compartment for chrome windows.
1970 if (aPrincipal->IsSystemPrincipal()) {
1971 return aOptions.setExistingCompartment(xpc::PrivilegedJunkScope());
1974 BrowsingContext* bc = aNewInner->GetBrowsingContext();
1975 if (bc->IsTop()) {
1976 // We're a toplevel load. Use a new zone. This way, when we do
1977 // zone-based compartment sharing we won't share compartments
1978 // across navigations.
1979 return aOptions.setNewCompartmentAndZone();
1982 // Find the in-process ancestor highest in the hierarchy.
1983 nsGlobalWindowInner* ancestor = nullptr;
1984 for (WindowContext* wc = bc->GetParentWindowContext(); wc;
1985 wc = wc->GetParentWindowContext()) {
1986 if (nsGlobalWindowInner* win = wc->GetInnerWindow()) {
1987 ancestor = win;
1991 // If we have an ancestor window, use its zone.
1992 if (ancestor && ancestor->GetGlobalJSObject()) {
1993 JS::Zone* zone = JS::GetObjectZone(ancestor->GetGlobalJSObject());
1994 // Now try to find an existing compartment that's same-origin
1995 // with our principal.
1996 CompartmentFinderState data(aPrincipal);
1997 JS_IterateCompartmentsInZone(aCx, zone, &data, FindSameOriginCompartment);
1998 if (data.compartment) {
1999 return aOptions.setExistingCompartment(data.compartment);
2001 return aOptions.setNewCompartmentInExistingZone(
2002 ancestor->GetGlobalJSObject());
2005 return aOptions.setNewCompartmentAndZone();
2009 * Create a new global object that will be used for an inner window.
2010 * Return the native global and an nsISupports 'holder' that can be used
2011 * to manage the lifetime of it.
2013 static nsresult CreateNativeGlobalForInner(
2014 JSContext* aCx, nsGlobalWindowInner* aNewInner, Document* aDocument,
2015 JS::MutableHandle<JSObject*> aGlobal, bool aIsSecureContext,
2016 bool aDefineSharedArrayBufferConstructor) {
2017 MOZ_ASSERT(aCx);
2018 MOZ_ASSERT(aNewInner);
2020 nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
2021 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
2022 MOZ_ASSERT(principal);
2024 // DOMWindow with nsEP is not supported, we have to make sure
2025 // no one creates one accidentally.
2026 nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(principal);
2027 MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
2029 JS::RealmOptions options;
2030 JS::RealmCreationOptions& creationOptions = options.creationOptions();
2032 SelectZone(aCx, principal, aNewInner, creationOptions);
2034 // Define the SharedArrayBuffer global constructor property only if shared
2035 // memory may be used and structured-cloned (e.g. through postMessage).
2037 // When the global constructor property isn't defined, the SharedArrayBuffer
2038 // constructor can still be reached through Web Assembly. Omitting the global
2039 // property just prevents feature-tests from being misled. See bug 1624266.
2040 creationOptions.setDefineSharedArrayBufferConstructor(
2041 aDefineSharedArrayBufferConstructor);
2043 xpc::InitGlobalObjectOptions(
2044 options, principal->IsSystemPrincipal(), aIsSecureContext,
2045 aDocument->ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC),
2046 aDocument->ShouldResistFingerprinting(RFPTarget::JSMathFdlibm),
2047 aDocument->ShouldResistFingerprinting(RFPTarget::JSLocale));
2049 // Determine if we need the Components object.
2050 bool needComponents = principal->IsSystemPrincipal();
2051 uint32_t flags = needComponents ? 0 : xpc::OMIT_COMPONENTS_OBJECT;
2052 flags |= xpc::DONT_FIRE_ONNEWGLOBALHOOK;
2054 if (!Window_Binding::Wrap(aCx, aNewInner, aNewInner, options,
2055 nsJSPrincipals::get(principal), aGlobal) ||
2056 !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
2057 return NS_ERROR_FAILURE;
2060 MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
2062 // Set the location information for the new global, so that tools like
2063 // about:memory may use that information
2064 xpc::SetLocationForGlobal(aGlobal, uri);
2066 if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) {
2067 return NS_ERROR_FAILURE;
2070 return NS_OK;
2073 nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
2074 nsISupports* aState,
2075 bool aForceReuseInnerWindow,
2076 WindowGlobalChild* aActor) {
2077 MOZ_ASSERT(mDocumentPrincipal == nullptr,
2078 "mDocumentPrincipal prematurely set!");
2079 MOZ_ASSERT(mDocumentCookiePrincipal == nullptr,
2080 "mDocumentCookiePrincipal prematurely set!");
2081 MOZ_ASSERT(mDocumentStoragePrincipal == nullptr,
2082 "mDocumentStoragePrincipal prematurely set!");
2083 MOZ_ASSERT(mDocumentPartitionedPrincipal == nullptr,
2084 "mDocumentPartitionedPrincipal prematurely set!");
2085 MOZ_ASSERT(aDocument);
2087 // Bail out early if we're in process of closing down the window.
2088 NS_ENSURE_STATE(!mCleanedUp);
2090 NS_ASSERTION(!GetCurrentInnerWindow() ||
2091 GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
2092 "Uh, mDoc doesn't match the current inner window "
2093 "document!");
2094 bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
2095 if (aForceReuseInnerWindow && !wouldReuseInnerWindow && mDoc &&
2096 mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
2097 NS_ERROR("Attempted forced inner window reuse while changing principal");
2098 return NS_ERROR_UNEXPECTED;
2101 if (!mBrowsingContext->AncestorsAreCurrent()) {
2102 return NS_ERROR_NOT_AVAILABLE;
2105 RefPtr<Document> oldDoc = mDoc;
2106 MOZ_RELEASE_ASSERT(oldDoc != aDocument);
2108 AutoJSAPI jsapi;
2109 jsapi.Init();
2110 JSContext* cx = jsapi.cx();
2112 // Check if we're anywhere near the stack limit before we reach the
2113 // transplanting code, since it has no good way to handle errors. This uses
2114 // the untrusted script limit, which is not strictly necessary since no
2115 // actual script should run.
2116 js::AutoCheckRecursionLimit recursion(cx);
2117 if (!recursion.checkConservativeDontReport(cx)) {
2118 NS_WARNING("Overrecursion in SetNewDocument");
2119 return NS_ERROR_FAILURE;
2122 if (!mDoc) {
2123 // First document load.
2125 // Get our private root. If it is equal to us, then we need to
2126 // attach our global key bindings that handles browser scrolling
2127 // and other browser commands.
2128 nsPIDOMWindowOuter* privateRoot = GetPrivateRoot();
2130 if (privateRoot == this) {
2131 RootWindowGlobalKeyListener::AttachKeyHandler(mChromeEventHandler);
2135 MaybeResetWindowName(aDocument);
2137 /* No mDocShell means we're already been partially closed down. When that
2138 happens, setting status isn't a big requirement, so don't. (Doesn't happen
2139 under normal circumstances, but bug 49615 describes a case.) */
2141 nsContentUtils::AddScriptRunner(
2142 NewRunnableMethod("nsGlobalWindowOuter::ClearStatus", this,
2143 &nsGlobalWindowOuter::ClearStatus));
2145 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
2146 // window (see bug 776497). Be safe.
2147 bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
2148 GetCurrentInnerWindowInternal(this);
2150 nsresult rv;
2152 // We set mDoc even though this is an outer window to avoid
2153 // having to *always* reach into the inner window to find the
2154 // document.
2155 mDoc = aDocument;
2157 nsDocShell::Cast(mDocShell)->MaybeRestoreWindowName();
2159 // We drop the print request for the old document on the floor, it never made
2160 // it. We don't close the window here either even if we were asked to.
2161 mShouldDelayPrintUntilAfterLoad = true;
2162 mDelayedCloseForPrinting = false;
2163 mDelayedPrintUntilAfterLoad = false;
2165 // Take this opportunity to clear mSuspendedDocs. Our old inner window is now
2166 // responsible for unsuspending it.
2167 mSuspendedDocs.Clear();
2169 #ifdef DEBUG
2170 mLastOpenedURI = aDocument->GetDocumentURI();
2171 #endif
2173 RefPtr<nsGlobalWindowInner> currentInner =
2174 GetCurrentInnerWindowInternal(this);
2176 if (currentInner && currentInner->mNavigator) {
2177 currentInner->mNavigator->OnNavigation();
2180 RefPtr<nsGlobalWindowInner> newInnerWindow;
2181 bool createdInnerWindow = false;
2183 bool thisChrome = IsChromeWindow();
2185 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
2186 NS_ASSERTION(!aState || wsh,
2187 "What kind of weird state are you giving me here?");
2189 bool doomCurrentInner = false;
2191 // Only non-gray (i.e. exposed to JS) objects should be assigned to
2192 // newInnerGlobal.
2193 JS::Rooted<JSObject*> newInnerGlobal(cx);
2194 if (reUseInnerWindow) {
2195 // We're reusing the current inner window.
2196 NS_ASSERTION(!currentInner->IsFrozen(),
2197 "We should never be reusing a shared inner window");
2198 newInnerWindow = currentInner;
2199 newInnerGlobal = currentInner->GetWrapper();
2201 // We're reusing the inner window, but this still counts as a navigation,
2202 // so all expandos and such defined on the outer window should go away.
2203 // Force all Xray wrappers to be recomputed.
2204 JS::Rooted<JSObject*> rootedObject(cx, GetWrapper());
2205 if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
2206 return NS_ERROR_FAILURE;
2209 // Inner windows are only reused for same-origin principals, but the
2210 // principals don't necessarily match exactly. Update the principal on the
2211 // realm to match the new document. NB: We don't just call
2212 // currentInner->RefreshRealmPrincipals() here because we haven't yet set
2213 // its mDoc to aDocument.
2214 JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal);
2215 #ifdef DEBUG
2216 bool sameOrigin = false;
2217 nsIPrincipal* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
2218 aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
2219 MOZ_ASSERT(sameOrigin);
2220 #endif
2221 JS::SetRealmPrincipals(realm,
2222 nsJSPrincipals::get(aDocument->NodePrincipal()));
2223 } else {
2224 if (aState) {
2225 newInnerWindow = wsh->GetInnerWindow();
2226 newInnerGlobal = newInnerWindow->GetWrapper();
2227 } else {
2228 newInnerWindow = nsGlobalWindowInner::Create(this, thisChrome, aActor);
2229 if (StaticPrefs::dom_timeout_defer_during_load()) {
2230 // ensure the initial loading state is known
2231 newInnerWindow->SetActiveLoadingState(
2232 aDocument->GetReadyStateEnum() ==
2233 Document::ReadyState::READYSTATE_LOADING);
2236 // The outer window is automatically treated as frozen when we
2237 // null out the inner window. As a result, initializing classes
2238 // on the new inner won't end up reaching into the old inner
2239 // window for classes etc.
2241 // [This happens with Object.prototype when XPConnect creates
2242 // a temporary global while initializing classes; the reason
2243 // being that xpconnect creates the temp global w/o a parent
2244 // and proto, which makes the JS engine look up classes in
2245 // cx->globalObject, i.e. this outer window].
2247 mInnerWindow = nullptr;
2249 mCreatingInnerWindow = true;
2251 // The SharedArrayBuffer global constructor property should not be present
2252 // in a fresh global object when shared memory objects aren't allowed
2253 // (because COOP/COEP support isn't enabled, or because COOP/COEP don't
2254 // act to isolate this page to a separate process).
2256 // Every script context we are initialized with must create a
2257 // new global.
2258 rv = CreateNativeGlobalForInner(
2259 cx, newInnerWindow, aDocument, &newInnerGlobal,
2260 ComputeIsSecureContext(aDocument),
2261 newInnerWindow->IsSharedMemoryAllowedInternal(
2262 aDocument->NodePrincipal()));
2263 NS_ASSERTION(
2264 NS_SUCCEEDED(rv) && newInnerGlobal &&
2265 newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
2266 "Failed to get script global");
2268 mCreatingInnerWindow = false;
2269 createdInnerWindow = true;
2271 NS_ENSURE_SUCCESS(rv, rv);
2274 if (currentInner && currentInner->GetWrapperPreserveColor()) {
2275 // Don't free objects on our current inner window if it's going to be
2276 // held in the bfcache.
2277 if (!currentInner->IsFrozen()) {
2278 doomCurrentInner = true;
2282 mInnerWindow = newInnerWindow;
2283 MOZ_ASSERT(mInnerWindow);
2284 mInnerWindow->TryToCacheTopInnerWindow();
2286 if (!GetWrapperPreserveColor()) {
2287 JS::Rooted<JSObject*> outer(
2288 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2289 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
2291 mBrowsingContext->CleanUpDanglingRemoteOuterWindowProxies(cx, &outer);
2292 MOZ_ASSERT(js::IsWindowProxy(outer));
2294 js::SetProxyReservedSlot(outer, OUTER_WINDOW_SLOT,
2295 JS::PrivateValue(ToSupports(this)));
2297 // Inform the nsJSContext, which is the canonical holder of the outer.
2298 mContext->SetWindowProxy(outer);
2300 SetWrapper(mContext->GetWindowProxy());
2301 } else {
2302 JS::Rooted<JSObject*> outerObject(
2303 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2304 if (!outerObject) {
2305 NS_ERROR("out of memory");
2306 return NS_ERROR_FAILURE;
2309 JS::Rooted<JSObject*> obj(cx, GetWrapper());
2311 MOZ_ASSERT(js::IsWindowProxy(obj));
2313 js::SetProxyReservedSlot(obj, OUTER_WINDOW_SLOT,
2314 JS::PrivateValue(nullptr));
2315 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
2316 JS::PrivateValue(nullptr));
2317 js::SetProxyReservedSlot(obj, HOLDER_WEAKMAP_SLOT, JS::UndefinedValue());
2319 outerObject = xpc::TransplantObjectNukingXrayWaiver(cx, obj, outerObject);
2321 if (!outerObject) {
2322 mBrowsingContext->ClearWindowProxy();
2323 NS_ERROR("unable to transplant wrappers, probably OOM");
2324 return NS_ERROR_FAILURE;
2327 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
2328 JS::PrivateValue(ToSupports(this)));
2330 SetWrapper(outerObject);
2332 MOZ_ASSERT(JS::GetNonCCWObjectGlobal(outerObject) == newInnerGlobal);
2334 // Inform the nsJSContext, which is the canonical holder of the outer.
2335 mContext->SetWindowProxy(outerObject);
2338 // Enter the new global's realm.
2339 JSAutoRealm ar(cx, GetWrapperPreserveColor());
2342 JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor());
2343 js::SetWindowProxy(cx, newInnerGlobal, outer);
2344 mBrowsingContext->SetWindowProxy(outer);
2347 // Set scriptability based on the state of the WindowContext.
2348 WindowContext* wc = mInnerWindow->GetWindowContext();
2349 bool allow =
2350 wc ? wc->CanExecuteScripts() : mBrowsingContext->CanExecuteScripts();
2351 xpc::Scriptability::Get(GetWrapperPreserveColor())
2352 .SetWindowAllowsScript(allow);
2354 if (!aState) {
2355 // Get the "window" property once so it will be cached on our inner. We
2356 // have to do this here, not in binding code, because this has to happen
2357 // after we've created the outer window proxy and stashed it in the outer
2358 // nsGlobalWindowOuter, so GetWrapperPreserveColor() on that outer
2359 // nsGlobalWindowOuter doesn't return null and
2360 // nsGlobalWindowOuter::OuterObject works correctly.
2361 JS::Rooted<JS::Value> unused(cx);
2362 if (!JS_GetProperty(cx, newInnerGlobal, "window", &unused)) {
2363 NS_ERROR("can't create the 'window' property");
2364 return NS_ERROR_FAILURE;
2367 // And same thing for the "self" property.
2368 if (!JS_GetProperty(cx, newInnerGlobal, "self", &unused)) {
2369 NS_ERROR("can't create the 'self' property");
2370 return NS_ERROR_FAILURE;
2375 JSAutoRealm ar(cx, GetWrapperPreserveColor());
2377 if (!aState && !reUseInnerWindow) {
2378 // Loading a new page and creating a new inner window, *not*
2379 // restoring from session history.
2381 // Now that both the the inner and outer windows are initialized
2382 // let the script context do its magic to hook them together.
2383 MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
2384 #ifdef DEBUG
2385 JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
2386 JS::Rooted<JSObject*> proto1(cx), proto2(cx);
2387 JS_GetPrototype(cx, rootedJSObject, &proto1);
2388 JS_GetPrototype(cx, newInnerGlobal, &proto2);
2389 NS_ASSERTION(proto1 == proto2,
2390 "outer and inner globals should have the same prototype");
2391 #endif
2393 mInnerWindow->SyncStateFromParentWindow();
2396 // Add an extra ref in case we release mContext during GC.
2397 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
2399 // Make sure the inner's document is set correctly before we call
2400 // SetScriptGlobalObject, because that might try to examine document-dependent
2401 // state. Unfortunately, we can't do some of the other clearing/resetting
2402 // work we do below until after SetScriptGlobalObject(), because it might
2403 // depend on the document having the right scope object.
2404 if (aState) {
2405 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument);
2406 } else {
2407 if (reUseInnerWindow) {
2408 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc != aDocument);
2410 newInnerWindow->mDoc = aDocument;
2413 aDocument->SetScriptGlobalObject(newInnerWindow);
2415 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc == aDocument);
2417 if (mBrowsingContext->IsTopContent()) {
2418 net::CookieJarSettings::Cast(aDocument->CookieJarSettings())
2419 ->SetTopLevelWindowContextId(aDocument->InnerWindowID());
2422 newInnerWindow->RefreshReduceTimerPrecisionCallerType();
2424 if (!aState) {
2425 if (reUseInnerWindow) {
2426 // The StorageAccess state may have changed. Invalidate the cached
2427 // StorageAllowed field, so that the next call to StorageAllowedForWindow
2428 // recomputes it.
2429 newInnerWindow->ClearStorageAllowedCache();
2431 // The storage objects contain the URL of the window. We have to
2432 // recreate them when the innerWindow is reused.
2433 newInnerWindow->mLocalStorage = nullptr;
2434 newInnerWindow->mSessionStorage = nullptr;
2435 newInnerWindow->mPerformance = nullptr;
2437 // This must be called after nullifying the internal objects because
2438 // here we could recreate them, calling the getter methods, and store
2439 // them into the JS slots. If we nullify them after, the slot values and
2440 // the objects will be out of sync.
2441 newInnerWindow->ClearDocumentDependentSlots(cx);
2442 } else {
2443 newInnerWindow->InitDocumentDependentState(cx);
2445 // Initialize DOM classes etc on the inner window.
2446 JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
2447 rv = kungFuDeathGrip->InitClasses(obj);
2448 NS_ENSURE_SUCCESS(rv, rv);
2451 // When replacing an initial about:blank document we call
2452 // ExecutionReady again to update the client creation URL.
2453 rv = newInnerWindow->ExecutionReady();
2454 NS_ENSURE_SUCCESS(rv, rv);
2456 if (mArguments) {
2457 newInnerWindow->DefineArgumentsProperty(mArguments);
2458 mArguments = nullptr;
2461 // Give the new inner window our chrome event handler (since it
2462 // doesn't have one).
2463 newInnerWindow->mChromeEventHandler = mChromeEventHandler;
2466 if (!aState && reUseInnerWindow) {
2467 // Notify our WindowGlobalChild that it has a new document. If `aState` was
2468 // passed, we're restoring the window from the BFCache, so the document
2469 // hasn't changed.
2470 // If we didn't have a window global child before, then initializing
2471 // it will have set all the required state, so we don't need to do
2472 // it again.
2473 mInnerWindow->GetWindowGlobalChild()->OnNewDocument(aDocument);
2476 // Update the current window for our BrowsingContext.
2477 RefPtr<BrowsingContext> bc = GetBrowsingContext();
2479 if (bc->IsOwnedByProcess()) {
2480 MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentInnerWindowId(mInnerWindow->WindowID()));
2483 // We no longer need the old inner window. Start its destruction if
2484 // its not being reused and clear our reference.
2485 if (doomCurrentInner) {
2486 currentInner->FreeInnerObjects();
2488 currentInner = nullptr;
2490 // We wait to fire the debugger hook until the window is all set up and hooked
2491 // up with the outer. See bug 969156.
2492 if (createdInnerWindow) {
2493 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2494 "nsGlobalWindowInner::FireOnNewGlobalObject", newInnerWindow,
2495 &nsGlobalWindowInner::FireOnNewGlobalObject));
2498 if (!newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
2499 // We should probably notify. However if this is the, arguably bad,
2500 // situation when we're creating a temporary non-chrome-about-blank
2501 // document in a chrome docshell, don't notify just yet. Instead wait
2502 // until we have a real chrome doc.
2503 const bool isContentAboutBlankInChromeDocshell = [&] {
2504 if (!mDocShell) {
2505 return false;
2508 RefPtr<BrowsingContext> bc = mDocShell->GetBrowsingContext();
2509 if (!bc || bc->GetType() != BrowsingContext::Type::Chrome) {
2510 return false;
2513 return !mDoc->NodePrincipal()->IsSystemPrincipal();
2514 }();
2516 if (!isContentAboutBlankInChromeDocshell) {
2517 newInnerWindow->mHasNotifiedGlobalCreated = true;
2518 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2519 "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
2520 &nsGlobalWindowOuter::DispatchDOMWindowCreated));
2524 PreloadLocalStorage();
2526 // Do this here rather than in say the Document constructor, since
2527 // we need a WindowContext available.
2528 mDoc->InitUseCounters();
2530 return NS_OK;
2533 /* static */
2534 void nsGlobalWindowOuter::PrepareForProcessChange(JSObject* aProxy) {
2535 JS::Rooted<JSObject*> localProxy(RootingCx(), aProxy);
2536 MOZ_ASSERT(js::IsWindowProxy(localProxy));
2538 RefPtr<nsGlobalWindowOuter> outerWindow =
2539 nsOuterWindowProxy::GetOuterWindow(localProxy);
2540 if (!outerWindow) {
2541 return;
2544 AutoJSAPI jsapi;
2545 jsapi.Init();
2546 JSContext* cx = jsapi.cx();
2548 JSAutoRealm ar(cx, localProxy);
2550 // Clear out existing references from the browsing context and outer window to
2551 // the proxy, and from the proxy to the outer window. These references will
2552 // become invalid once the proxy is transplanted. Clearing the window proxy
2553 // from the browsing context is also necessary to indicate that it is for an
2554 // out of process window.
2555 outerWindow->ClearWrapper(localProxy);
2556 RefPtr<BrowsingContext> bc = outerWindow->GetBrowsingContext();
2557 MOZ_ASSERT(bc);
2558 MOZ_ASSERT(bc->GetWindowProxy() == localProxy);
2559 bc->ClearWindowProxy();
2560 js::SetProxyReservedSlot(localProxy, OUTER_WINDOW_SLOT,
2561 JS::PrivateValue(nullptr));
2562 js::SetProxyReservedSlot(localProxy, HOLDER_WEAKMAP_SLOT,
2563 JS::UndefinedValue());
2565 // Create a new remote outer window proxy, and transplant to it.
2566 JS::Rooted<JSObject*> remoteProxy(cx);
2568 if (!mozilla::dom::GetRemoteOuterWindowProxy(cx, bc, localProxy,
2569 &remoteProxy)) {
2570 MOZ_CRASH("PrepareForProcessChange GetRemoteOuterWindowProxy");
2573 if (!xpc::TransplantObjectNukingXrayWaiver(cx, localProxy, remoteProxy)) {
2574 MOZ_CRASH("PrepareForProcessChange TransplantObject");
2578 void nsGlobalWindowOuter::PreloadLocalStorage() {
2579 if (!Storage::StoragePrefIsEnabled()) {
2580 return;
2583 if (IsChromeWindow()) {
2584 return;
2587 nsIPrincipal* principal = GetPrincipal();
2588 nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
2589 if (!principal || !storagePrincipal) {
2590 return;
2593 nsresult rv;
2595 nsCOMPtr<nsIDOMStorageManager> storageManager =
2596 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
2597 if (NS_FAILED(rv)) {
2598 return;
2601 // private browsing windows do not persist local storage to disk so we should
2602 // only try to precache storage when we're not a private browsing window.
2603 if (principal->GetPrivateBrowsingId() == 0) {
2604 RefPtr<Storage> storage;
2605 rv = storageManager->PrecacheStorage(principal, storagePrincipal,
2606 getter_AddRefs(storage));
2607 if (NS_SUCCEEDED(rv)) {
2608 mLocalStorage = storage;
2613 void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
2614 if (!mDoc) {
2615 return;
2618 // Fire DOMWindowCreated at chrome event listeners
2619 nsContentUtils::DispatchChromeEvent(mDoc, mDoc, u"DOMWindowCreated"_ns,
2620 CanBubble::eYes, Cancelable::eNo);
2622 nsCOMPtr<nsIObserverService> observerService =
2623 mozilla::services::GetObserverService();
2625 // The event dispatching could possibly cause docshell destory, and
2626 // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(),
2627 // so check it again here.
2628 if (observerService && mDoc) {
2629 nsAutoString origin;
2630 nsIPrincipal* principal = mDoc->NodePrincipal();
2631 nsContentUtils::GetWebExposedOriginSerialization(principal, origin);
2632 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
2633 principal->IsSystemPrincipal()
2634 ? "chrome-document-global-created"
2635 : "content-document-global-created",
2636 origin.get());
2640 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u""_ns); }
2642 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
2643 MOZ_ASSERT(aDocShell);
2645 if (aDocShell == mDocShell) {
2646 return;
2649 mDocShell = aDocShell;
2650 mBrowsingContext = aDocShell->GetBrowsingContext();
2652 RefPtr<BrowsingContext> parentContext = mBrowsingContext->GetParent();
2654 MOZ_RELEASE_ASSERT(!parentContext ||
2655 GetBrowsingContextGroup() == parentContext->Group());
2657 mTopLevelOuterContentWindow = mBrowsingContext->IsTopContent();
2659 // Get our enclosing chrome shell and retrieve its global window impl, so
2660 // that we can do some forwarding to the chrome document.
2661 RefPtr<EventTarget> chromeEventHandler;
2662 mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2663 mChromeEventHandler = chromeEventHandler;
2664 if (!mChromeEventHandler) {
2665 // We have no chrome event handler. If we have a parent,
2666 // get our chrome event handler from the parent. If
2667 // we don't have a parent, then we need to make a new
2668 // window root object that will function as a chrome event
2669 // handler and receive all events that occur anywhere inside
2670 // our window.
2671 nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetInProcessParent();
2672 if (parentWindow.get() != this) {
2673 mChromeEventHandler = parentWindow->GetChromeEventHandler();
2674 } else {
2675 mChromeEventHandler = NS_NewWindowRoot(this);
2676 mIsRootOuterWindow = true;
2680 SetIsBackgroundInternal(!mBrowsingContext->IsActive());
2683 void nsGlobalWindowOuter::DetachFromDocShell(bool aIsBeingDiscarded) {
2684 // DetachFromDocShell means the window is being torn down. Drop our
2685 // reference to the script context, allowing it to be deleted
2686 // later. Meanwhile, keep our weak reference to the script object
2687 // so that it can be retrieved later (until it is finalized by the JS GC).
2689 // Call FreeInnerObjects on all inner windows, not just the current
2690 // one, since some could be held by WindowStateHolder objects that
2691 // are GC-owned.
2692 RefPtr<nsGlobalWindowInner> inner;
2693 for (PRCList* node = PR_LIST_HEAD(this); node != this;
2694 node = PR_NEXT_LINK(inner)) {
2695 // This cast is safe because `node != this`. Non-this nodes are inner
2696 // windows.
2697 inner = static_cast<nsGlobalWindowInner*>(node);
2698 MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == this);
2699 inner->FreeInnerObjects();
2702 // Don't report that we were detached to the nsWindowMemoryReporter, as it
2703 // only tracks inner windows.
2705 NotifyWindowIDDestroyed("outer-window-destroyed");
2707 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(this);
2709 if (currentInner) {
2710 NS_ASSERTION(mDoc, "Must have doc!");
2712 // Remember the document's principal and URI.
2713 mDocumentPrincipal = mDoc->NodePrincipal();
2714 mDocumentCookiePrincipal = mDoc->EffectiveCookiePrincipal();
2715 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
2716 mDocumentPartitionedPrincipal = mDoc->PartitionedPrincipal();
2717 mDocumentURI = mDoc->GetDocumentURI();
2719 // Release our document reference
2720 DropOuterWindowDocs();
2723 ClearControllers();
2725 mChromeEventHandler = nullptr; // force release now
2727 if (mContext) {
2728 // When we're about to destroy a top level content window
2729 // (for example a tab), we trigger a full GC by passing null as the last
2730 // param. We also trigger a full GC for chrome windows.
2731 nsJSContext::PokeGC(JS::GCReason::SET_DOC_SHELL,
2732 (mTopLevelOuterContentWindow || mIsChrome)
2733 ? nullptr
2734 : GetWrapperPreserveColor());
2735 mContext = nullptr;
2738 if (aIsBeingDiscarded) {
2739 // If our BrowsingContext is being discarded, make a note that our current
2740 // inner window was active at the time it went away.
2741 if (nsGlobalWindowInner* currentInner =
2742 GetCurrentInnerWindowInternal(this)) {
2743 currentInner->SetWasCurrentInnerWindow();
2747 mDocShell = nullptr;
2748 mBrowsingContext->ClearDocShell();
2750 CleanUp();
2753 void nsGlobalWindowOuter::UpdateParentTarget() {
2754 // NOTE: This method is nearly identical to
2755 // nsGlobalWindowInner::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2756 // UPDATE THE OTHER ONE TOO! The one difference is that this method updates
2757 // mMessageManager as well, which inner windows don't have.
2759 // Try to get our frame element's tab child global (its in-process message
2760 // manager). If that fails, fall back to the chrome event handler's tab
2761 // child global, and if it doesn't have one, just use the chrome event
2762 // handler itself.
2764 nsCOMPtr<Element> frameElement = GetFrameElementInternal();
2765 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2767 if (!mMessageManager) {
2768 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
2769 if (topWin) {
2770 frameElement = topWin->GetFrameElementInternal();
2771 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2775 if (!mMessageManager) {
2776 mMessageManager =
2777 nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2780 if (mMessageManager) {
2781 mParentTarget = mMessageManager;
2782 } else {
2783 mParentTarget = mChromeEventHandler;
2787 EventTarget* nsGlobalWindowOuter::GetTargetForEventTargetChain() {
2788 return GetCurrentInnerWindowInternal(this);
2791 void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2792 MOZ_CRASH("The outer window should not be part of an event path");
2795 bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() {
2796 if (!nsContentUtils::GetCurrentJSContext()) {
2797 return false; // non-scripted caller.
2800 BrowsingContextGroup* group = GetBrowsingContextGroup();
2801 if (!group) {
2802 return true;
2805 return group->DialogsAreBeingAbused();
2808 bool nsGlobalWindowOuter::AreDialogsEnabled() {
2809 BrowsingContextGroup* group = mBrowsingContext->Group();
2810 if (!group) {
2811 NS_ERROR("AreDialogsEnabled() called without a browsing context group?");
2812 return false;
2815 // Dialogs are blocked if the content viewer is hidden
2816 if (mDocShell) {
2817 nsCOMPtr<nsIDocumentViewer> viewer;
2818 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2820 bool isHidden;
2821 viewer->GetIsHidden(&isHidden);
2822 if (isHidden) {
2823 return false;
2827 // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS
2828 // (or if we have no document, of course). Which document? Who knows; the
2829 // spec is daft. See <https://github.com/whatwg/html/issues/1206>. For now
2830 // just go ahead and check mDoc, since in everything except edge cases in
2831 // which a frame is allow-same-origin but not allow-scripts and is being poked
2832 // at by some other window this should be the right thing anyway.
2833 if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
2834 return false;
2837 return group->GetAreDialogsEnabled();
2840 bool nsGlobalWindowOuter::ConfirmDialogIfNeeded() {
2841 NS_ENSURE_TRUE(mDocShell, false);
2842 nsCOMPtr<nsIPromptService> promptSvc =
2843 do_GetService("@mozilla.org/prompter;1");
2845 if (!promptSvc) {
2846 return true;
2849 // Reset popup state while opening a modal dialog, and firing events
2850 // about the dialog, to prevent the current state from being active
2851 // the whole time a modal dialog is open.
2852 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
2854 bool disableDialog = false;
2855 nsAutoString label, title;
2856 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
2857 "ScriptDialogLabel", label);
2858 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
2859 "ScriptDialogPreventTitle", title);
2860 promptSvc->Confirm(this, title.get(), label.get(), &disableDialog);
2861 if (disableDialog) {
2862 DisableDialogs();
2863 return false;
2866 return true;
2869 void nsGlobalWindowOuter::DisableDialogs() {
2870 BrowsingContextGroup* group = mBrowsingContext->Group();
2871 if (!group) {
2872 NS_ERROR("DisableDialogs() called without a browsing context group?");
2873 return;
2876 if (group) {
2877 group->SetAreDialogsEnabled(false);
2881 void nsGlobalWindowOuter::EnableDialogs() {
2882 BrowsingContextGroup* group = mBrowsingContext->Group();
2883 if (!group) {
2884 NS_ERROR("EnableDialogs() called without a browsing context group?");
2885 return;
2888 if (group) {
2889 group->SetAreDialogsEnabled(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(this);
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::GetEffectiveCookiePrincipal() {
2951 if (mDoc) {
2952 // If we have a document, get the principal from the document
2953 return mDoc->EffectiveCookiePrincipal();
2956 if (mDocumentCookiePrincipal) {
2957 return mDocumentCookiePrincipal;
2960 // If we don't have a cookie principal and we don't have a document we ask
2961 // the parent window for the cookie principal.
2963 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2964 do_QueryInterface(GetInProcessParentInternal());
2966 if (objPrincipal) {
2967 return objPrincipal->GetEffectiveCookiePrincipal();
2970 return nullptr;
2973 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
2974 if (mDoc) {
2975 // If we have a document, get the principal from the document
2976 return mDoc->EffectiveStoragePrincipal();
2979 if (mDocumentStoragePrincipal) {
2980 return mDocumentStoragePrincipal;
2983 // If we don't have a storage principal and we don't have a document we ask
2984 // the parent window for the storage principal.
2986 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2987 do_QueryInterface(GetInProcessParentInternal());
2989 if (objPrincipal) {
2990 return objPrincipal->GetEffectiveStoragePrincipal();
2993 return nullptr;
2996 nsIPrincipal* nsGlobalWindowOuter::PartitionedPrincipal() {
2997 if (mDoc) {
2998 // If we have a document, get the principal from the document
2999 return mDoc->PartitionedPrincipal();
3002 if (mDocumentPartitionedPrincipal) {
3003 return mDocumentPartitionedPrincipal;
3006 // If we don't have a partitioned principal and we don't have a document we
3007 // ask the parent window for the partitioned principal.
3009 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
3010 do_QueryInterface(GetInProcessParentInternal());
3012 if (objPrincipal) {
3013 return objPrincipal->PartitionedPrincipal();
3016 return nullptr;
3019 //*****************************************************************************
3020 // nsGlobalWindowOuter::nsIDOMWindow
3021 //*****************************************************************************
3023 Element* nsPIDOMWindowOuter::GetFrameElementInternal() const {
3024 return mFrameElement;
3027 void nsPIDOMWindowOuter::SetFrameElementInternal(Element* aFrameElement) {
3028 mFrameElement = aFrameElement;
3031 Navigator* nsGlobalWindowOuter::GetNavigator() {
3032 FORWARD_TO_INNER(Navigator, (), nullptr);
3035 nsScreen* nsGlobalWindowOuter::GetScreen() {
3036 FORWARD_TO_INNER(Screen, (), nullptr);
3039 void nsPIDOMWindowOuter::ActivateMediaComponents() {
3040 if (!ShouldDelayMediaFromStart()) {
3041 return;
3043 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
3044 ("nsPIDOMWindowOuter, ActiveMediaComponents, "
3045 "no longer to delay media from start, this = %p\n",
3046 this));
3047 if (BrowsingContext* bc = GetBrowsingContext()) {
3048 Unused << bc->Top()->SetShouldDelayMediaFromStart(false);
3050 NotifyResumingDelayedMedia();
3053 bool nsPIDOMWindowOuter::ShouldDelayMediaFromStart() const {
3054 BrowsingContext* bc = GetBrowsingContext();
3055 return bc && bc->Top()->GetShouldDelayMediaFromStart();
3058 void nsPIDOMWindowOuter::NotifyResumingDelayedMedia() {
3059 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
3060 if (service) {
3061 service->NotifyResumingDelayedMedia(this);
3065 bool nsPIDOMWindowOuter::GetAudioMuted() const {
3066 BrowsingContext* bc = GetBrowsingContext();
3067 return bc && bc->Top()->GetMuted();
3070 void nsPIDOMWindowOuter::RefreshMediaElementsVolume() {
3071 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
3072 if (service) {
3073 // TODO: RefreshAgentsVolume can probably be simplified further.
3074 service->RefreshAgentsVolume(this, 1.0f, GetAudioMuted());
3078 mozilla::dom::BrowsingContextGroup*
3079 nsPIDOMWindowOuter::GetBrowsingContextGroup() const {
3080 return mBrowsingContext ? mBrowsingContext->Group() : nullptr;
3083 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetParentOuter() {
3084 BrowsingContext* bc = GetBrowsingContext();
3085 return bc ? bc->GetParent(IgnoreErrors()) : nullptr;
3089 * GetInProcessScriptableParent used to be called when a script read
3090 * window.parent. Under Fission, that is now handled by
3091 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
3092 * an actual global window. This method still exists for legacy callers which
3093 * relied on the old logic, and require in-process windows. However, it only
3094 * works correctly when no out-of-process frames exist between this window and
3095 * the top-level window, so it should not be used in new code.
3097 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
3098 * mozbrowser> boundaries, so if |this| is contained by an <iframe
3099 * mozbrowser>, we will return |this| as its own parent.
3101 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParent() {
3102 if (!mDocShell) {
3103 return nullptr;
3106 if (BrowsingContext* parentBC = GetBrowsingContext()->GetParent()) {
3107 if (nsCOMPtr<nsPIDOMWindowOuter> parent = parentBC->GetDOMWindow()) {
3108 return parent;
3111 return this;
3115 * Behavies identically to GetInProcessScriptableParent extept that it returns
3116 * null if GetInProcessScriptableParent would return this window.
3118 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableParentOrNull() {
3119 nsPIDOMWindowOuter* parent = GetInProcessScriptableParent();
3120 return (nsGlobalWindowOuter::Cast(parent) == this) ? nullptr : parent;
3123 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessParent() {
3124 if (!mDocShell) {
3125 return nullptr;
3128 if (auto* parentBC = GetBrowsingContext()->GetParent()) {
3129 if (auto* parent = parentBC->GetDOMWindow()) {
3130 return do_AddRef(parent);
3133 return do_AddRef(this);
3136 static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsIURI* aURIBeingLoaded,
3137 nsPIDOMWindowOuter** aTop, bool aScriptable,
3138 bool aExcludingExtensionAccessibleContentFrames) {
3139 *aTop = nullptr;
3141 MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames, !aScriptable);
3143 // Walk up the parent chain.
3145 nsCOMPtr<nsPIDOMWindowOuter> prevParent = aWin;
3146 nsCOMPtr<nsPIDOMWindowOuter> parent = aWin;
3147 do {
3148 if (!parent) {
3149 break;
3152 prevParent = parent;
3154 if (aScriptable) {
3155 parent = parent->GetInProcessScriptableParent();
3156 } else {
3157 parent = parent->GetInProcessParent();
3160 if (aExcludingExtensionAccessibleContentFrames) {
3161 if (auto* p = nsGlobalWindowOuter::Cast(parent)) {
3162 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal(p);
3163 nsIURI* uri = prevParent->GetDocumentURI();
3164 if (!uri) {
3165 // If our parent doesn't have a URI yet, we have a document that is in
3166 // the process of being loaded. In that case, our caller is
3167 // responsible for passing in the URI for the document that is being
3168 // loaded, so we fall back to using that URI here.
3169 uri = aURIBeingLoaded;
3172 if (currentInner && uri) {
3173 // If we find an inner window, we better find the uri for the current
3174 // window we're looking at. If we can't find it directly, it is the
3175 // responsibility of our caller to provide it to us.
3176 MOZ_DIAGNOSTIC_ASSERT(uri);
3178 // If the new parent has permission to load the current page, we're
3179 // at a moz-extension:// frame which has a host permission that allows
3180 // it to load the document that we've loaded. In that case, stop at
3181 // this frame and consider it the top-level frame.
3183 // Note that it's possible for the set of URIs accepted by
3184 // AddonAllowsLoad() to change at runtime, but we don't need to cache
3185 // the result of this check, since the important consumer of this code
3186 // (which is nsIHttpChannelInternal.topWindowURI) already caches the
3187 // result after computing it the first time.
3188 if (BasePrincipal::Cast(p->GetPrincipal())
3189 ->AddonAllowsLoad(uri, true)) {
3190 parent = prevParent;
3191 break;
3197 } while (parent != prevParent);
3199 if (parent) {
3200 parent.swap(*aTop);
3203 return NS_OK;
3207 * GetInProcessScriptableTop used to be called when a script read window.top.
3208 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
3209 * a WindowProxyHolder rather than an actual global window. This method still
3210 * exists for legacy callers which relied on the old logic, and require
3211 * in-process windows. However, it only works correctly when no out-of-process
3212 * frames exist between this window and the top-level window, so it should not
3213 * be used in new code.
3215 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
3216 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
3217 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
3218 * window.
3220 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessScriptableTop() {
3221 nsCOMPtr<nsPIDOMWindowOuter> window;
3222 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
3223 /* aScriptable = */ true,
3224 /* aExcludingExtensionAccessibleContentFrames = */ false);
3225 return window.get();
3228 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetInProcessTop() {
3229 nsCOMPtr<nsPIDOMWindowOuter> window;
3230 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
3231 /* aScriptable = */ false,
3232 /* aExcludingExtensionAccessibleContentFrames = */ false);
3233 return window.forget();
3236 already_AddRefed<nsPIDOMWindowOuter>
3237 nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames(
3238 nsIURI* aURIBeingLoaded) {
3239 // There is a parent-process equivalent of this in DocumentLoadListener.cpp
3240 // GetTopWindowExcludingExtensionAccessibleContentFrames
3241 nsCOMPtr<nsPIDOMWindowOuter> window;
3242 GetTopImpl(this, aURIBeingLoaded, getter_AddRefs(window),
3243 /* aScriptable = */ false,
3244 /* aExcludingExtensionAccessibleContentFrames = */ true);
3245 return window.forget();
3248 void nsGlobalWindowOuter::GetContentOuter(JSContext* aCx,
3249 JS::MutableHandle<JSObject*> aRetval,
3250 CallerType aCallerType,
3251 ErrorResult& aError) {
3252 RefPtr<BrowsingContext> content = GetContentInternal(aCallerType, aError);
3253 if (aError.Failed()) {
3254 return;
3257 if (!content) {
3258 aRetval.set(nullptr);
3259 return;
3262 JS::Rooted<JS::Value> val(aCx);
3263 if (!ToJSValue(aCx, WindowProxyHolder{content}, &val)) {
3264 aError.Throw(NS_ERROR_UNEXPECTED);
3265 return;
3268 MOZ_ASSERT(val.isObjectOrNull());
3269 aRetval.set(val.toObjectOrNull());
3272 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetContentInternal(
3273 CallerType aCallerType, ErrorResult& aError) {
3274 // First check for a named frame named "content"
3275 if (RefPtr<BrowsingContext> named = GetChildWindow(u"content"_ns)) {
3276 return named.forget();
3279 // If we're in the parent process, and being called by system code, `content`
3280 // should return the current primary content frame (if it's in-process).
3282 // We return `nullptr` if the current primary content frame is out-of-process,
3283 // rather than a remote window proxy, as that is the existing behaviour as of
3284 // bug 1597437.
3285 if (XRE_IsParentProcess() && aCallerType == CallerType::System) {
3286 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
3287 if (!treeOwner) {
3288 aError.Throw(NS_ERROR_FAILURE);
3289 return nullptr;
3292 nsCOMPtr<nsIDocShellTreeItem> primaryContent;
3293 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
3294 if (!primaryContent) {
3295 return nullptr;
3298 return do_AddRef(primaryContent->GetBrowsingContext());
3301 // For legacy untrusted callers we always return the same value as
3302 // `window.top`
3303 if (mDoc && aCallerType != CallerType::System) {
3304 mDoc->WarnOnceAbout(DeprecatedOperations::eWindowContentUntrusted);
3307 MOZ_ASSERT(mBrowsingContext->IsContent());
3308 return do_AddRef(mBrowsingContext->Top());
3311 nsresult nsGlobalWindowOuter::GetPrompter(nsIPrompt** aPrompt) {
3312 if (!mDocShell) return NS_ERROR_FAILURE;
3314 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
3315 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
3317 prompter.forget(aPrompt);
3318 return NS_OK;
3321 bool nsGlobalWindowOuter::GetClosedOuter() {
3322 // If someone called close(), or if we don't have a docshell, we're closed.
3323 return mIsClosed || !mDocShell;
3326 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); }
3328 Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter(
3329 uint32_t aIndex) {
3330 BrowsingContext* bc = GetBrowsingContext();
3331 NS_ENSURE_TRUE(bc, nullptr);
3333 Span<RefPtr<BrowsingContext>> children = bc->NonSyntheticChildren();
3335 if (aIndex < children.Length()) {
3336 return WindowProxyHolder(children[aIndex]);
3338 return nullptr;
3341 nsIControllers* nsGlobalWindowOuter::GetControllersOuter(ErrorResult& aError) {
3342 if (!mControllers) {
3343 mControllers = new nsXULControllers();
3344 if (!mControllers) {
3345 aError.Throw(NS_ERROR_FAILURE);
3346 return nullptr;
3349 // Add in the default controller
3350 RefPtr<nsBaseCommandController> commandController =
3351 nsBaseCommandController::CreateWindowController();
3352 if (!commandController) {
3353 aError.Throw(NS_ERROR_FAILURE);
3354 return nullptr;
3357 mControllers->InsertControllerAt(0, commandController);
3358 commandController->SetCommandContext(static_cast<nsIDOMWindow*>(this));
3361 return mControllers;
3364 nsresult nsGlobalWindowOuter::GetControllers(nsIControllers** aResult) {
3365 FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED);
3368 already_AddRefed<BrowsingContext>
3369 nsGlobalWindowOuter::GetOpenerBrowsingContext() {
3370 RefPtr<BrowsingContext> opener = GetBrowsingContext()->GetOpener();
3371 MOZ_DIAGNOSTIC_ASSERT(!opener ||
3372 opener->Group() == GetBrowsingContext()->Group());
3373 if (!opener || opener->Group() != GetBrowsingContext()->Group()) {
3374 return nullptr;
3377 // Catch the case where we're chrome but the opener is not...
3378 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode() &&
3379 GetPrincipal() == nsContentUtils::GetSystemPrincipal()) {
3380 auto* openerWin = nsGlobalWindowOuter::Cast(opener->GetDOMWindow());
3381 if (!openerWin ||
3382 openerWin->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
3383 return nullptr;
3387 return opener.forget();
3390 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSameProcessOpener() {
3391 if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
3392 return opener->GetDOMWindow();
3394 return nullptr;
3397 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpenerWindowOuter() {
3398 if (RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext()) {
3399 return WindowProxyHolder(std::move(opener));
3401 return nullptr;
3404 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetOpener() {
3405 return GetOpenerWindowOuter();
3408 void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) {
3409 aStatus = mStatus;
3412 void nsGlobalWindowOuter::SetStatusOuter(const nsAString& aStatus) {
3413 mStatus = aStatus;
3415 // We don't support displaying window.status in the UI, so there's nothing
3416 // left to do here.
3419 void nsGlobalWindowOuter::GetNameOuter(nsAString& aName) {
3420 if (mDocShell) {
3421 mDocShell->GetName(aName);
3425 void nsGlobalWindowOuter::SetNameOuter(const nsAString& aName,
3426 mozilla::ErrorResult& aError) {
3427 if (mDocShell) {
3428 aError = mDocShell->SetName(aName);
3432 // NOTE: The idea of this function is that it should return the same as
3433 // nsPresContext::CSSToDeviceScale() if it was in aWindow synchronously. For
3434 // that, we use the UnscaledDevicePixelsPerCSSPixel() (which contains the device
3435 // scale and the OS zoom scale) and then account for the browsing context full
3436 // zoom. See the declaration of this function for context about why this is
3437 // needed.
3438 CSSToLayoutDeviceScale nsGlobalWindowOuter::CSSToDevScaleForBaseWindow(
3439 nsIBaseWindow* aWindow) {
3440 MOZ_ASSERT(aWindow);
3441 auto scale = aWindow->UnscaledDevicePixelsPerCSSPixel();
3442 if (mBrowsingContext) {
3443 scale.scale *= mBrowsingContext->FullZoom();
3445 return scale;
3448 nsresult nsGlobalWindowOuter::GetInnerSize(CSSSize& aSize) {
3449 EnsureSizeAndPositionUpToDate();
3451 NS_ENSURE_STATE(mDocShell);
3453 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3454 PresShell* presShell = mDocShell->GetPresShell();
3456 if (!presContext || !presShell) {
3457 aSize = {};
3458 return NS_OK;
3461 // Whether or not the css viewport has been overridden, we can get the
3462 // correct value by looking at the visible area of the presContext.
3463 if (RefPtr<nsViewManager> viewManager = presShell->GetViewManager()) {
3464 viewManager->FlushDelayedResize();
3467 // FIXME: Bug 1598487 - Return the layout viewport instead of the ICB.
3468 nsSize viewportSize = presContext->GetVisibleArea().Size();
3469 if (presContext->GetDynamicToolbarState() == DynamicToolbarState::Collapsed) {
3470 viewportSize =
3471 nsLayoutUtils::ExpandHeightForViewportUnits(presContext, viewportSize);
3474 aSize = CSSPixel::FromAppUnits(viewportSize);
3476 switch (StaticPrefs::dom_innerSize_rounding()) {
3477 case 1:
3478 aSize.width = std::roundf(aSize.width);
3479 aSize.height = std::roundf(aSize.height);
3480 break;
3481 case 2:
3482 aSize.width = std::truncf(aSize.width);
3483 aSize.height = std::truncf(aSize.height);
3484 break;
3485 default:
3486 break;
3489 return NS_OK;
3492 double nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult& aError) {
3493 CSSSize size;
3494 aError = GetInnerSize(size);
3495 return size.width;
3498 nsresult nsGlobalWindowOuter::GetInnerWidth(double* aInnerWidth) {
3499 FORWARD_TO_INNER(GetInnerWidth, (aInnerWidth), NS_ERROR_UNEXPECTED);
3502 double nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult& aError) {
3503 CSSSize size;
3504 aError = GetInnerSize(size);
3505 return size.height;
3508 nsresult nsGlobalWindowOuter::GetInnerHeight(double* aInnerHeight) {
3509 FORWARD_TO_INNER(GetInnerHeight, (aInnerHeight), NS_ERROR_UNEXPECTED);
3512 CSSIntSize nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType,
3513 ErrorResult& aError) {
3514 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType,
3515 RFPTarget::WindowOuterSize)) {
3516 CSSSize size;
3517 aError = GetInnerSize(size);
3518 return RoundedToInt(size);
3521 // Windows showing documents in RDM panes and any subframes within them
3522 // return the simulated device size.
3523 if (mDoc) {
3524 Maybe<CSSIntSize> deviceSize = GetRDMDeviceSize(*mDoc);
3525 if (deviceSize.isSome()) {
3526 return *deviceSize;
3530 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3531 if (!treeOwnerAsWin) {
3532 aError.Throw(NS_ERROR_FAILURE);
3533 return {};
3536 return RoundedToInt(treeOwnerAsWin->GetSize() /
3537 CSSToDevScaleForBaseWindow(treeOwnerAsWin));
3540 int32_t nsGlobalWindowOuter::GetOuterWidthOuter(CallerType aCallerType,
3541 ErrorResult& aError) {
3542 return GetOuterSize(aCallerType, aError).width;
3545 int32_t nsGlobalWindowOuter::GetOuterHeightOuter(CallerType aCallerType,
3546 ErrorResult& aError) {
3547 return GetOuterSize(aCallerType, aError).height;
3550 CSSPoint nsGlobalWindowOuter::ScreenEdgeSlop() {
3551 if (NS_WARN_IF(!mDocShell)) {
3552 return {};
3554 RefPtr<nsPresContext> pc = mDocShell->GetPresContext();
3555 if (NS_WARN_IF(!pc)) {
3556 return {};
3558 nsCOMPtr<nsIWidget> widget = GetMainWidget();
3559 if (NS_WARN_IF(!widget)) {
3560 return {};
3562 LayoutDeviceIntPoint pt = widget->GetScreenEdgeSlop();
3563 auto auPoint =
3564 LayoutDeviceIntPoint::ToAppUnits(pt, pc->AppUnitsPerDevPixel());
3565 return CSSPoint::FromAppUnits(auPoint);
3568 CSSIntPoint nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType,
3569 ErrorResult& aError) {
3570 // When resisting fingerprinting, always return (0,0)
3571 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType,
3572 RFPTarget::WindowScreenXY)) {
3573 return CSSIntPoint(0, 0);
3576 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3577 if (!treeOwnerAsWin) {
3578 aError.Throw(NS_ERROR_FAILURE);
3579 return CSSIntPoint(0, 0);
3582 LayoutDeviceIntPoint windowPos;
3583 aError = treeOwnerAsWin->GetPosition(&windowPos.x.value, &windowPos.y.value);
3585 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3586 if (!presContext) {
3587 // XXX Fishy LayoutDevice to CSS conversion?
3588 return CSSIntPoint(windowPos.x, windowPos.y);
3591 nsDeviceContext* context = presContext->DeviceContext();
3592 auto windowPosAppUnits = LayoutDeviceIntPoint::ToAppUnits(
3593 windowPos, context->AppUnitsPerDevPixel());
3594 return CSSIntPoint::FromAppUnitsRounded(windowPosAppUnits);
3597 int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType,
3598 ErrorResult& aError) {
3599 return GetScreenXY(aCallerType, aError).x;
3602 nsRect nsGlobalWindowOuter::GetInnerScreenRect() {
3603 if (!mDocShell) {
3604 return nsRect();
3607 EnsureSizeAndPositionUpToDate();
3609 if (!mDocShell) {
3610 return nsRect();
3613 PresShell* presShell = mDocShell->GetPresShell();
3614 if (!presShell) {
3615 return nsRect();
3617 nsIFrame* rootFrame = presShell->GetRootFrame();
3618 if (!rootFrame) {
3619 return nsRect();
3622 return rootFrame->GetScreenRectInAppUnits();
3625 Maybe<CSSIntSize> nsGlobalWindowOuter::GetRDMDeviceSize(
3626 const Document& aDocument) {
3627 // RDM device size should reflect the simulated device resolution, and
3628 // be independent of any full zoom or resolution zoom applied to the
3629 // content. To get this value, we get the "unscaled" browser child size,
3630 // and divide by the full zoom. "Unscaled" in this case means unscaled
3631 // from device to screen but it has been affected (multiplied) by the
3632 // full zoom and we need to compensate for that.
3633 MOZ_RELEASE_ASSERT(NS_IsMainThread());
3635 // Bug 1576256: This does not work for cross-process subframes.
3636 const Document* topInProcessContentDoc =
3637 aDocument.GetTopLevelContentDocumentIfSameProcess();
3638 BrowsingContext* bc = topInProcessContentDoc
3639 ? topInProcessContentDoc->GetBrowsingContext()
3640 : nullptr;
3641 if (bc && bc->InRDMPane()) {
3642 nsIDocShell* docShell = topInProcessContentDoc->GetDocShell();
3643 if (docShell) {
3644 nsPresContext* presContext = docShell->GetPresContext();
3645 if (presContext) {
3646 nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild();
3647 if (child) {
3648 // We intentionally use GetFullZoom here instead of
3649 // GetDeviceFullZoom, because the unscaledInnerSize is based
3650 // on the full zoom and not the device full zoom (which is
3651 // rounded to result in integer device pixels).
3652 float zoom = presContext->GetFullZoom();
3653 BrowserChild* bc = static_cast<BrowserChild*>(child.get());
3654 CSSSize unscaledSize = bc->GetUnscaledInnerSize();
3655 return Some(CSSIntSize(gfx::RoundedToInt(unscaledSize / zoom)));
3660 return Nothing();
3663 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType) {
3664 // When resisting fingerprinting, always return 0.
3665 if (nsIGlobalObject::ShouldResistFingerprinting(
3666 aCallerType, RFPTarget::WindowInnerScreenXY)) {
3667 return 0.0;
3670 nsRect r = GetInnerScreenRect();
3671 return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
3674 float nsGlobalWindowOuter::GetMozInnerScreenYOuter(CallerType aCallerType) {
3675 // Return 0 to prevent fingerprinting.
3676 if (nsIGlobalObject::ShouldResistFingerprinting(
3677 aCallerType, RFPTarget::WindowInnerScreenXY)) {
3678 return 0.0;
3681 nsRect r = GetInnerScreenRect();
3682 return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
3685 int32_t nsGlobalWindowOuter::GetScreenYOuter(CallerType aCallerType,
3686 ErrorResult& aError) {
3687 return GetScreenXY(aCallerType, aError).y;
3690 // NOTE: Arguments to this function should have values scaled to
3691 // CSS pixels, not device pixels.
3692 void nsGlobalWindowOuter::CheckSecurityWidthAndHeight(int32_t* aWidth,
3693 int32_t* aHeight,
3694 CallerType aCallerType) {
3695 if (aCallerType != CallerType::System) {
3696 // if attempting to resize the window, hide any open popups
3697 nsContentUtils::HidePopupsInDocument(mDoc);
3700 // This one is easy. Just ensure the variable is greater than 100;
3701 if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
3702 // Check security state for use in determing window dimensions
3704 if (aCallerType != CallerType::System) {
3705 // sec check failed
3706 if (aWidth && *aWidth < 100) {
3707 *aWidth = 100;
3709 if (aHeight && *aHeight < 100) {
3710 *aHeight = 100;
3716 // NOTE: Arguments to this function should have values in app units
3717 void nsGlobalWindowOuter::SetCSSViewportWidthAndHeight(nscoord aInnerWidth,
3718 nscoord aInnerHeight) {
3719 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3721 nsRect shellArea = presContext->GetVisibleArea();
3722 shellArea.SetHeight(aInnerHeight);
3723 shellArea.SetWidth(aInnerWidth);
3725 // FIXME(emilio): This doesn't seem to be ok, this doesn't reflow or
3726 // anything... Should go through PresShell::ResizeReflow.
3728 // But I don't think this can be reached by content, as we don't allow to set
3729 // inner{Width,Height}.
3730 presContext->SetVisibleArea(shellArea);
3733 // NOTE: Arguments to this function should have values scaled to
3734 // CSS pixels, not device pixels.
3735 void nsGlobalWindowOuter::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop,
3736 CallerType aCallerType) {
3737 // This one is harder. We have to get the screen size and window dimensions.
3739 // Check security state for use in determing window dimensions
3741 if (aCallerType != CallerType::System) {
3742 // if attempting to move the window, hide any open popups
3743 nsContentUtils::HidePopupsInDocument(mDoc);
3745 if (nsGlobalWindowOuter* rootWindow =
3746 nsGlobalWindowOuter::Cast(GetPrivateRoot())) {
3747 rootWindow->FlushPendingNotifications(FlushType::Layout);
3750 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3752 RefPtr<nsScreen> screen = GetScreen();
3754 if (treeOwnerAsWin && screen) {
3755 CSSToLayoutDeviceScale scale = CSSToDevScaleForBaseWindow(treeOwnerAsWin);
3756 CSSIntRect winRect =
3757 CSSIntRect::Round(treeOwnerAsWin->GetPositionAndSize() / scale);
3759 // Get the screen dimensions
3760 // XXX This should use nsIScreenManager once it's fully fleshed out.
3761 int32_t screenLeft = screen->AvailLeft();
3762 int32_t screenWidth = screen->AvailWidth();
3763 int32_t screenHeight = screen->AvailHeight();
3764 #if defined(XP_MACOSX)
3765 /* The mac's coordinate system is different from the assumed Windows'
3766 system. It offsets by the height of the menubar so that a window
3767 placed at (0,0) will be entirely visible. Unfortunately that
3768 correction is made elsewhere (in Widget) and the meaning of
3769 the Avail... coordinates is overloaded. Here we allow a window
3770 to be placed at (0,0) because it does make sense to do so.
3772 int32_t screenTop = screen->Top();
3773 #else
3774 int32_t screenTop = screen->AvailTop();
3775 #endif
3777 if (aLeft) {
3778 if (screenLeft + screenWidth < *aLeft + winRect.width)
3779 *aLeft = screenLeft + screenWidth - winRect.width;
3780 if (screenLeft > *aLeft) *aLeft = screenLeft;
3782 if (aTop) {
3783 if (screenTop + screenHeight < *aTop + winRect.height)
3784 *aTop = screenTop + screenHeight - winRect.height;
3785 if (screenTop > *aTop) *aTop = screenTop;
3787 } else {
3788 if (aLeft) *aLeft = 0;
3789 if (aTop) *aTop = 0;
3794 int32_t nsGlobalWindowOuter::GetScrollBoundaryOuter(Side aSide) {
3795 FlushPendingNotifications(FlushType::Layout);
3796 if (nsIScrollableFrame* sf = GetScrollFrame()) {
3797 return nsPresContext::AppUnitsToIntCSSPixels(
3798 sf->GetScrollRange().Edge(aSide));
3800 return 0;
3803 CSSPoint nsGlobalWindowOuter::GetScrollXY(bool aDoFlush) {
3804 if (aDoFlush) {
3805 FlushPendingNotifications(FlushType::Layout);
3806 } else {
3807 EnsureSizeAndPositionUpToDate();
3810 nsIScrollableFrame* sf = GetScrollFrame();
3811 if (!sf) {
3812 return CSSIntPoint(0, 0);
3815 nsPoint scrollPos = sf->GetScrollPosition();
3816 if (scrollPos != nsPoint(0, 0) && !aDoFlush) {
3817 // Oh, well. This is the expensive case -- the window is scrolled and we
3818 // didn't actually flush yet. Repeat, but with a flush, since the content
3819 // may get shorter and hence our scroll position may decrease.
3820 return GetScrollXY(true);
3823 return CSSPoint::FromAppUnits(scrollPos);
3826 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x; }
3828 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
3830 uint32_t nsGlobalWindowOuter::Length() {
3831 BrowsingContext* bc = GetBrowsingContext();
3832 return bc ? bc->NonSyntheticChildren().Length() : 0;
3835 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
3836 BrowsingContext* bc = GetBrowsingContext();
3837 return bc ? bc->GetTop(IgnoreErrors()) : nullptr;
3840 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
3841 const nsAString& aName) {
3842 NS_ENSURE_TRUE(mBrowsingContext, nullptr);
3843 NS_ENSURE_TRUE(mInnerWindow, nullptr);
3844 NS_ENSURE_TRUE(mInnerWindow->GetWindowGlobalChild(), nullptr);
3846 return do_AddRef(mBrowsingContext->FindChildWithName(
3847 aName, *mInnerWindow->GetWindowGlobalChild()));
3850 bool nsGlobalWindowOuter::DispatchCustomEvent(
3851 const nsAString& aEventName, ChromeOnlyDispatch aChromeOnlyDispatch) {
3852 bool defaultActionEnabled = true;
3854 if (aChromeOnlyDispatch == ChromeOnlyDispatch::eYes) {
3855 nsContentUtils::DispatchEventOnlyToChrome(mDoc, this, aEventName,
3856 CanBubble::eYes, Cancelable::eYes,
3857 &defaultActionEnabled);
3858 } else {
3859 nsContentUtils::DispatchTrustedEvent(mDoc, this, aEventName,
3860 CanBubble::eYes, Cancelable::eYes,
3861 &defaultActionEnabled);
3864 return defaultActionEnabled;
3867 bool nsGlobalWindowOuter::DispatchResizeEvent(const CSSIntSize& aSize) {
3868 ErrorResult res;
3869 RefPtr<Event> domEvent =
3870 mDoc->CreateEvent(u"CustomEvent"_ns, CallerType::System, res);
3871 if (res.Failed()) {
3872 return false;
3875 // We don't init the AutoJSAPI with ourselves because we don't want it
3876 // reporting errors to our onerror handlers.
3877 AutoJSAPI jsapi;
3878 jsapi.Init();
3879 JSContext* cx = jsapi.cx();
3880 JSAutoRealm ar(cx, GetWrapperPreserveColor());
3882 DOMWindowResizeEventDetail detail;
3883 detail.mWidth = aSize.width;
3884 detail.mHeight = aSize.height;
3885 JS::Rooted<JS::Value> detailValue(cx);
3886 if (!ToJSValue(cx, detail, &detailValue)) {
3887 return false;
3890 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
3891 customEvent->InitCustomEvent(cx, u"DOMWindowResize"_ns,
3892 /* aCanBubble = */ true,
3893 /* aCancelable = */ true, detailValue);
3895 domEvent->SetTrusted(true);
3896 domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
3898 nsCOMPtr<EventTarget> target = this;
3899 domEvent->SetTarget(target);
3901 return target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors());
3904 bool nsGlobalWindowOuter::WindowExists(const nsAString& aName,
3905 bool aForceNoOpener,
3906 bool aLookForCallerOnJSStack) {
3907 MOZ_ASSERT(mDocShell, "Must have docshell");
3909 if (aForceNoOpener) {
3910 return aName.LowerCaseEqualsLiteral("_self") ||
3911 aName.LowerCaseEqualsLiteral("_top") ||
3912 aName.LowerCaseEqualsLiteral("_parent");
3915 if (WindowGlobalChild* wgc = mInnerWindow->GetWindowGlobalChild()) {
3916 return wgc->FindBrowsingContextWithName(aName, aLookForCallerOnJSStack);
3918 return false;
3921 already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() {
3922 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3924 nsCOMPtr<nsIWidget> widget;
3926 if (treeOwnerAsWin) {
3927 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
3930 return widget.forget();
3933 nsIWidget* nsGlobalWindowOuter::GetNearestWidget() const {
3934 nsIDocShell* docShell = GetDocShell();
3935 if (!docShell) {
3936 return nullptr;
3938 PresShell* presShell = docShell->GetPresShell();
3939 if (!presShell) {
3940 return nullptr;
3942 nsIFrame* rootFrame = presShell->GetRootFrame();
3943 if (!rootFrame) {
3944 return nullptr;
3946 return rootFrame->GetView()->GetNearestWidget(nullptr);
3949 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen,
3950 mozilla::ErrorResult& aError) {
3951 aError =
3952 SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullscreen);
3955 nsresult nsGlobalWindowOuter::SetFullScreen(bool aFullscreen) {
3956 return SetFullscreenInternal(FullscreenReason::ForFullscreenMode,
3957 aFullscreen);
3960 static void FinishDOMFullscreenChange(Document* aDoc, bool aInDOMFullscreen) {
3961 if (aInDOMFullscreen) {
3962 // Ask the document to handle any pending DOM fullscreen change.
3963 if (!Document::HandlePendingFullscreenRequests(aDoc)) {
3964 // If we don't end up having anything in fullscreen,
3965 // async request exiting fullscreen.
3966 Document::AsyncExitFullscreen(aDoc);
3968 } else {
3969 // If the window is leaving fullscreen state, also ask the document
3970 // to exit from DOM Fullscreen.
3971 Document::ExitFullscreenInDocTree(aDoc);
3975 struct FullscreenTransitionDuration {
3976 // The unit of the durations is millisecond
3977 uint16_t mFadeIn = 0;
3978 uint16_t mFadeOut = 0;
3979 bool IsSuppressed() const { return mFadeIn == 0 && mFadeOut == 0; }
3982 static void GetFullscreenTransitionDuration(
3983 bool aEnterFullscreen, FullscreenTransitionDuration* aDuration) {
3984 const char* pref = aEnterFullscreen
3985 ? "full-screen-api.transition-duration.enter"
3986 : "full-screen-api.transition-duration.leave";
3987 nsAutoCString prefValue;
3988 Preferences::GetCString(pref, prefValue);
3989 if (!prefValue.IsEmpty()) {
3990 sscanf(prefValue.get(), "%hu%hu", &aDuration->mFadeIn,
3991 &aDuration->mFadeOut);
3995 class FullscreenTransitionTask : public Runnable {
3996 public:
3997 FullscreenTransitionTask(const FullscreenTransitionDuration& aDuration,
3998 nsGlobalWindowOuter* aWindow, bool aFullscreen,
3999 nsIWidget* aWidget, nsISupports* aTransitionData)
4000 : mozilla::Runnable("FullscreenTransitionTask"),
4001 mWindow(aWindow),
4002 mWidget(aWidget),
4003 mTransitionData(aTransitionData),
4004 mDuration(aDuration),
4005 mStage(eBeforeToggle),
4006 mFullscreen(aFullscreen) {}
4008 NS_IMETHOD Run() override;
4010 private:
4011 ~FullscreenTransitionTask() override = default;
4014 * The flow of fullscreen transition:
4016 * parent process | child process
4017 * ----------------------------------------------------------------
4019 * | request/exit fullscreen
4020 * <-----|
4021 * BeforeToggle stage |
4023 * ToggleFullscreen stage *1 |----->
4024 * | HandleFullscreenRequests
4026 * <-----| MozAfterPaint event
4027 * AfterToggle stage *2 |
4029 * End stage |
4031 * Note we also start a timer at *1 so that if we don't get MozAfterPaint
4032 * from the child process in time, we continue going to *2.
4034 enum Stage {
4035 // BeforeToggle stage happens before we enter or leave fullscreen
4036 // state. In this stage, the task triggers the pre-toggle fullscreen
4037 // transition on the widget.
4038 eBeforeToggle,
4039 // ToggleFullscreen stage actually executes the fullscreen toggle,
4040 // and wait for the next paint on the content to continue.
4041 eToggleFullscreen,
4042 // AfterToggle stage happens after we toggle the fullscreen state.
4043 // In this stage, the task triggers the post-toggle fullscreen
4044 // transition on the widget.
4045 eAfterToggle,
4046 // End stage is triggered after the final transition finishes.
4047 eEnd
4050 class Observer final : public nsIObserver, public nsINamed {
4051 public:
4052 NS_DECL_ISUPPORTS
4053 NS_DECL_NSIOBSERVER
4054 NS_DECL_NSINAMED
4056 explicit Observer(FullscreenTransitionTask* aTask) : mTask(aTask) {}
4058 private:
4059 ~Observer() = default;
4061 RefPtr<FullscreenTransitionTask> mTask;
4064 static const char* const kPaintedTopic;
4066 RefPtr<nsGlobalWindowOuter> mWindow;
4067 nsCOMPtr<nsIWidget> mWidget;
4068 nsCOMPtr<nsITimer> mTimer;
4069 nsCOMPtr<nsISupports> mTransitionData;
4071 TimeStamp mFullscreenChangeStartTime;
4072 FullscreenTransitionDuration mDuration;
4073 Stage mStage;
4074 bool mFullscreen;
4077 const char* const FullscreenTransitionTask::kPaintedTopic =
4078 "fullscreen-painted";
4080 NS_IMETHODIMP
4081 FullscreenTransitionTask::Run() {
4082 Stage stage = mStage;
4083 mStage = Stage(mStage + 1);
4084 if (MOZ_UNLIKELY(mWidget->Destroyed())) {
4085 // If the widget has been destroyed before we get here, don't try to
4086 // do anything more. Just let it go and release ourselves.
4087 NS_WARNING("The widget to fullscreen has been destroyed");
4088 mWindow->mIsInFullScreenTransition = false;
4089 return NS_OK;
4091 if (stage == eBeforeToggle) {
4092 PROFILER_MARKER_UNTYPED("Fullscreen transition start", DOM);
4094 mWindow->mIsInFullScreenTransition = true;
4096 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4097 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
4098 obs->NotifyObservers(nullptr, "fullscreen-transition-start", nullptr);
4100 mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
4101 mDuration.mFadeIn, mTransitionData,
4102 this);
4103 } else if (stage == eToggleFullscreen) {
4104 PROFILER_MARKER_UNTYPED("Fullscreen toggle start", DOM);
4105 mFullscreenChangeStartTime = TimeStamp::Now();
4106 // Toggle the fullscreen state on the widget
4107 if (!mWindow->SetWidgetFullscreen(FullscreenReason::ForFullscreenAPI,
4108 mFullscreen, mWidget)) {
4109 // Fail to setup the widget, call FinishFullscreenChange to
4110 // complete fullscreen change directly.
4111 mWindow->FinishFullscreenChange(mFullscreen);
4113 // Set observer for the next content paint.
4114 nsCOMPtr<nsIObserver> observer = new Observer(this);
4115 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4116 obs->AddObserver(observer, kPaintedTopic, false);
4117 // There are several edge cases where we may never get the paint
4118 // notification, including:
4119 // 1. the window/tab is closed before the next paint;
4120 // 2. the user has switched to another tab before we get here.
4121 // Completely fixing those cases seems to be tricky, and since they
4122 // should rarely happen, it probably isn't worth to fix. Hence we
4123 // simply add a timeout here to ensure we never hang forever.
4124 // In addition, if the page is complicated or the machine is less
4125 // powerful, layout could take a long time, in which case, staying
4126 // in black screen for that long could hurt user experience even
4127 // more than exposing an intermediate state.
4128 uint32_t timeout =
4129 Preferences::GetUint("full-screen-api.transition.timeout", 1000);
4130 NS_NewTimerWithObserver(getter_AddRefs(mTimer), observer, timeout,
4131 nsITimer::TYPE_ONE_SHOT);
4132 } else if (stage == eAfterToggle) {
4133 Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
4134 mFullscreenChangeStartTime);
4135 mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
4136 mDuration.mFadeOut, mTransitionData,
4137 this);
4138 } else if (stage == eEnd) {
4139 PROFILER_MARKER_UNTYPED("Fullscreen transition end", DOM);
4141 mWindow->mIsInFullScreenTransition = false;
4143 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4144 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
4145 obs->NotifyObservers(nullptr, "fullscreen-transition-end", nullptr);
4147 mWidget->CleanupFullscreenTransition();
4149 return NS_OK;
4152 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer, nsIObserver, nsINamed)
4154 NS_IMETHODIMP
4155 FullscreenTransitionTask::Observer::Observe(nsISupports* aSubject,
4156 const char* aTopic,
4157 const char16_t* aData) {
4158 bool shouldContinue = false;
4159 if (strcmp(aTopic, FullscreenTransitionTask::kPaintedTopic) == 0) {
4160 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aSubject));
4161 nsCOMPtr<nsIWidget> widget =
4162 win ? nsGlobalWindowInner::Cast(win)->GetMainWidget() : nullptr;
4163 if (widget == mTask->mWidget) {
4164 // The paint notification arrives first. Cancel the timer.
4165 mTask->mTimer->Cancel();
4166 shouldContinue = true;
4167 PROFILER_MARKER_UNTYPED("Fullscreen toggle end", DOM);
4169 } else {
4170 #ifdef DEBUG
4171 MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0,
4172 "Should only get fullscreen-painted or timer-callback");
4173 nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
4174 MOZ_ASSERT(timer && timer == mTask->mTimer,
4175 "Should only trigger this with the timer the task created");
4176 #endif
4177 shouldContinue = true;
4178 PROFILER_MARKER_UNTYPED("Fullscreen toggle timeout", DOM);
4180 if (shouldContinue) {
4181 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4182 obs->RemoveObserver(this, kPaintedTopic);
4183 mTask->mTimer = nullptr;
4184 mTask->Run();
4186 return NS_OK;
4189 NS_IMETHODIMP
4190 FullscreenTransitionTask::Observer::GetName(nsACString& aName) {
4191 aName.AssignLiteral("FullscreenTransitionTask");
4192 return NS_OK;
4195 static bool MakeWidgetFullscreen(nsGlobalWindowOuter* aWindow,
4196 FullscreenReason aReason, bool aFullscreen) {
4197 nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget();
4198 if (!widget) {
4199 return false;
4202 FullscreenTransitionDuration duration;
4203 bool performTransition = false;
4204 nsCOMPtr<nsISupports> transitionData;
4205 if (aReason == FullscreenReason::ForFullscreenAPI) {
4206 GetFullscreenTransitionDuration(aFullscreen, &duration);
4207 if (!duration.IsSuppressed()) {
4208 performTransition = widget->PrepareForFullscreenTransition(
4209 getter_AddRefs(transitionData));
4213 if (!performTransition) {
4214 return aWindow->SetWidgetFullscreen(aReason, aFullscreen, widget);
4217 nsCOMPtr<nsIRunnable> task = new FullscreenTransitionTask(
4218 duration, aWindow, aFullscreen, widget, transitionData);
4219 task->Run();
4220 return true;
4223 nsresult nsGlobalWindowOuter::ProcessWidgetFullscreenRequest(
4224 FullscreenReason aReason, bool aFullscreen) {
4225 mInProcessFullscreenRequest.emplace(aReason, aFullscreen);
4227 // Prevent chrome documents which are still loading from resizing
4228 // the window after we set fullscreen mode.
4229 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4230 nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwnerAsWin));
4231 if (aFullscreen && appWin) {
4232 appWin->SetIntrinsicallySized(false);
4235 // Sometimes we don't want the top-level widget to actually go fullscreen:
4236 // - in the B2G desktop client, we don't want the emulated screen dimensions
4237 // to appear to increase when entering fullscreen mode; we just want the
4238 // content to fill the entire client area of the emulator window.
4239 // - in FxR Desktop, we don't want fullscreen to take over the monitor, but
4240 // instead we want fullscreen to fill the FxR window in the the headset.
4241 if (!StaticPrefs::full_screen_api_ignore_widgets() &&
4242 !mForceFullScreenInWidget) {
4243 if (MakeWidgetFullscreen(this, aReason, aFullscreen)) {
4244 // The rest of code for switching fullscreen is in nsGlobalWindowOuter::
4245 // FinishFullscreenChange() which will be called after sizemodechange
4246 // event is dispatched.
4247 return NS_OK;
4251 #if defined(NIGHTLY_BUILD) && defined(XP_WIN)
4252 if (FxRWindowManager::GetInstance()->IsFxRWindow(mWindowID)) {
4253 mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
4254 shmem.SendFullscreenState(mWindowID, aFullscreen);
4256 #endif // NIGHTLY_BUILD && XP_WIN
4257 FinishFullscreenChange(aFullscreen);
4258 return NS_OK;
4261 nsresult nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason,
4262 bool aFullscreen) {
4263 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
4264 "Requires safe to run script as it "
4265 "may call FinishDOMFullscreenChange");
4267 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
4269 MOZ_ASSERT(
4270 aReason != FullscreenReason::ForForceExitFullscreen || !aFullscreen,
4271 "FullscreenReason::ForForceExitFullscreen can "
4272 "only be used with exiting fullscreen");
4274 // Only chrome can change our fullscreen mode. Otherwise, the state
4275 // can only be changed for DOM fullscreen.
4276 if (aReason == FullscreenReason::ForFullscreenMode &&
4277 !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
4278 return NS_OK;
4281 // SetFullscreen needs to be called on the root window, so get that
4282 // via the DocShell tree, and if we are not already the root,
4283 // call SetFullscreen on that window instead.
4284 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4285 mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
4286 nsCOMPtr<nsPIDOMWindowOuter> window =
4287 rootItem ? rootItem->GetWindow() : nullptr;
4288 if (!window) return NS_ERROR_FAILURE;
4289 if (rootItem != mDocShell)
4290 return window->SetFullscreenInternal(aReason, aFullscreen);
4292 // make sure we don't try to set full screen on a non-chrome window,
4293 // which might happen in embedding world
4294 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome)
4295 return NS_ERROR_FAILURE;
4297 // FullscreenReason::ForForceExitFullscreen can only be used with exiting
4298 // fullscreen
4299 MOZ_ASSERT_IF(
4300 mFullscreen.isSome(),
4301 mFullscreen.value() != FullscreenReason::ForForceExitFullscreen);
4303 // If we are already in full screen mode, just return, we don't care about the
4304 // reason here, because,
4305 // - If we are in fullscreen mode due to browser fullscreen mode, requesting
4306 // DOM fullscreen does not change anything.
4307 // - If we are in fullscreen mode due to DOM fullscreen, requesting browser
4308 // fullscreen should not change anything, either. Note that we should not
4309 // update reason to ForFullscreenMode, otherwise the subsequent DOM
4310 // fullscreen exit will be ignored and user will be confused. And ideally
4311 // this should never happen as `window.fullscreen` returns `true` for DOM
4312 // fullscreen as well.
4313 if (mFullscreen.isSome() == aFullscreen) {
4314 // How come we get browser fullscreen request while we are already in DOM
4315 // fullscreen?
4316 MOZ_ASSERT_IF(aFullscreen && aReason == FullscreenReason::ForFullscreenMode,
4317 mFullscreen.value() != FullscreenReason::ForFullscreenAPI);
4318 return NS_OK;
4321 // Note that although entering DOM fullscreen could also cause
4322 // consequential calls to this method, those calls will be skipped
4323 // at the condition above.
4324 if (aReason == FullscreenReason::ForFullscreenMode) {
4325 if (!aFullscreen && mFullscreen &&
4326 mFullscreen.value() == FullscreenReason::ForFullscreenAPI) {
4327 // If we are exiting fullscreen mode, but we actually didn't
4328 // entered browser fullscreen mode, the fullscreen state was only for
4329 // the Fullscreen API. Change the reason here so that we can
4330 // perform transition for it.
4331 aReason = FullscreenReason::ForFullscreenAPI;
4333 } else {
4334 // If we are exiting from DOM fullscreen while we initially make
4335 // the window fullscreen because of browser fullscreen mode, don't restore
4336 // the window. But we still need to exit the DOM fullscreen state.
4337 if (!aFullscreen && mFullscreen &&
4338 mFullscreen.value() == FullscreenReason::ForFullscreenMode) {
4339 // If there is a in-process fullscreen request, FinishDOMFullscreenChange
4340 // will be called when the request is finished.
4341 if (!mInProcessFullscreenRequest.isSome()) {
4342 FinishDOMFullscreenChange(mDoc, false);
4344 return NS_OK;
4348 // Set this before so if widget sends an event indicating its
4349 // gone full screen, the state trap above works.
4350 if (aFullscreen) {
4351 mFullscreen.emplace(aReason);
4352 } else {
4353 mFullscreen.reset();
4356 // If we are in process of fullscreen request, only keep the latest fullscreen
4357 // state, we will sync up later while the processing request is finished.
4358 if (mInProcessFullscreenRequest.isSome()) {
4359 mFullscreenHasChangedDuringProcessing = true;
4360 return NS_OK;
4363 return ProcessWidgetFullscreenRequest(aReason, aFullscreen);
4366 // Support a per-window, dynamic equivalent of enabling
4367 // full-screen-api.ignore-widgets
4368 void nsGlobalWindowOuter::ForceFullScreenInWidget() {
4369 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
4371 mForceFullScreenInWidget = true;
4374 bool nsGlobalWindowOuter::SetWidgetFullscreen(FullscreenReason aReason,
4375 bool aIsFullscreen,
4376 nsIWidget* aWidget) {
4377 MOZ_ASSERT(this == GetInProcessTopInternal(),
4378 "Only topmost window should call this");
4379 MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this");
4380 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
4382 if (!NS_WARN_IF(!IsChromeWindow())) {
4383 if (!NS_WARN_IF(mChromeFields.mFullscreenPresShell)) {
4384 if (PresShell* presShell = mDocShell->GetPresShell()) {
4385 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
4386 mChromeFields.mFullscreenPresShell = do_GetWeakReference(presShell);
4387 MOZ_ASSERT(mChromeFields.mFullscreenPresShell);
4388 rd->SetIsResizeSuppressed();
4389 rd->Freeze();
4394 nsresult rv = aReason == FullscreenReason::ForFullscreenMode
4396 // If we enter fullscreen for fullscreen mode, we want
4397 // the native system behavior.
4398 aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen)
4399 : aWidget->MakeFullScreen(aIsFullscreen);
4400 return NS_SUCCEEDED(rv);
4403 /* virtual */
4404 void nsGlobalWindowOuter::FullscreenWillChange(bool aIsFullscreen) {
4405 if (!mInProcessFullscreenRequest.isSome()) {
4406 // If there is no in-process fullscreen request, the fullscreen state change
4407 // is triggered from the OS directly, e.g. user use built-in window button
4408 // to enter/exit fullscreen on macOS.
4409 mInProcessFullscreenRequest.emplace(FullscreenReason::ForFullscreenMode,
4410 aIsFullscreen);
4411 if (mFullscreen.isSome() != aIsFullscreen) {
4412 if (aIsFullscreen) {
4413 mFullscreen.emplace(FullscreenReason::ForFullscreenMode);
4414 } else {
4415 mFullscreen.reset();
4417 } else {
4418 // It is possible that FullscreenWillChange is notified with current
4419 // fullscreen state, e.g. browser goes into fullscreen when widget
4420 // fullscreen is prevented, and then user triggers fullscreen from the OS
4421 // directly again.
4422 MOZ_ASSERT(StaticPrefs::full_screen_api_ignore_widgets() ||
4423 mForceFullScreenInWidget,
4424 "This should only happen when widget fullscreen is prevented");
4427 if (aIsFullscreen) {
4428 DispatchCustomEvent(u"willenterfullscreen"_ns, ChromeOnlyDispatch::eYes);
4429 } else {
4430 DispatchCustomEvent(u"willexitfullscreen"_ns, ChromeOnlyDispatch::eYes);
4434 /* virtual */
4435 void nsGlobalWindowOuter::FinishFullscreenChange(bool aIsFullscreen) {
4436 mozilla::Maybe<FullscreenRequest> currentInProcessRequest =
4437 std::move(mInProcessFullscreenRequest);
4438 if (!mFullscreenHasChangedDuringProcessing &&
4439 aIsFullscreen != mFullscreen.isSome()) {
4440 NS_WARNING("Failed to toggle fullscreen state of the widget");
4441 // We failed to make the widget enter fullscreen.
4442 // Stop further changes and restore the state.
4443 if (!aIsFullscreen) {
4444 mFullscreen.reset();
4445 } else {
4446 #ifndef XP_MACOSX
4447 MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?");
4448 #endif
4449 // Restore fullscreen state with FullscreenReason::ForFullscreenAPI reason
4450 // in order to make subsequent DOM fullscreen exit request can exit
4451 // browser fullscreen mode.
4452 mFullscreen.emplace(FullscreenReason::ForFullscreenAPI);
4454 return;
4457 // Note that we must call this to toggle the DOM fullscreen state
4458 // of the document before dispatching the "fullscreen" event, so
4459 // that the chrome can distinguish between browser fullscreen mode
4460 // and DOM fullscreen.
4461 FinishDOMFullscreenChange(mDoc, aIsFullscreen);
4463 // dispatch a "fullscreen" DOM event so that XUL apps can
4464 // respond visually if we are kicked into full screen mode
4465 DispatchCustomEvent(u"fullscreen"_ns, ChromeOnlyDispatch::eYes);
4467 if (!NS_WARN_IF(!IsChromeWindow())) {
4468 if (RefPtr<PresShell> presShell =
4469 do_QueryReferent(mChromeFields.mFullscreenPresShell)) {
4470 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
4471 rd->Thaw();
4473 mChromeFields.mFullscreenPresShell = nullptr;
4477 // If fullscreen state has changed during processing fullscreen request, we
4478 // need to ensure widget matches our latest fullscreen state here.
4479 if (mFullscreenHasChangedDuringProcessing) {
4480 mFullscreenHasChangedDuringProcessing = false;
4481 // Widget doesn't care about the reason that makes it entering/exiting
4482 // fullscreen, so here we just need to ensure the fullscreen state is
4483 // matched.
4484 if (aIsFullscreen != mFullscreen.isSome()) {
4485 // If we end up need to exit fullscreen, use the same reason that brings
4486 // us into fullscreen mode, so that we will perform the same fullscreen
4487 // transistion effect for exiting.
4488 ProcessWidgetFullscreenRequest(
4489 mFullscreen.isSome() ? mFullscreen.value()
4490 : currentInProcessRequest.value().mReason,
4491 mFullscreen.isSome());
4496 /* virtual */
4497 void nsGlobalWindowOuter::MacFullscreenMenubarOverlapChanged(
4498 mozilla::DesktopCoord aOverlapAmount) {
4499 ErrorResult res;
4500 RefPtr<Event> domEvent =
4501 mDoc->CreateEvent(u"CustomEvent"_ns, CallerType::System, res);
4502 if (res.Failed()) {
4503 return;
4506 AutoJSAPI jsapi;
4507 jsapi.Init();
4508 JSContext* cx = jsapi.cx();
4509 JSAutoRealm ar(cx, GetWrapperPreserveColor());
4511 JS::Rooted<JS::Value> detailValue(cx);
4512 if (!ToJSValue(cx, aOverlapAmount, &detailValue)) {
4513 return;
4516 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
4517 customEvent->InitCustomEvent(cx, u"MacFullscreenMenubarRevealUpdate"_ns,
4518 /* aCanBubble = */ true,
4519 /* aCancelable = */ true, detailValue);
4520 domEvent->SetTrusted(true);
4521 domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
4523 nsCOMPtr<EventTarget> target = this;
4524 domEvent->SetTarget(target);
4526 target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors());
4529 bool nsGlobalWindowOuter::Fullscreen() const {
4530 NS_ENSURE_TRUE(mDocShell, mFullscreen.isSome());
4532 // Get the fullscreen value of the root window, to always have the value
4533 // accurate, even when called from content.
4534 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4535 mDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
4536 if (rootItem == mDocShell) {
4537 if (!XRE_IsContentProcess()) {
4538 // We are the root window. Return our internal value.
4539 return mFullscreen.isSome();
4541 if (nsCOMPtr<nsIWidget> widget = GetNearestWidget()) {
4542 // We are in content process, figure out the value from
4543 // the sizemode of the puppet widget.
4544 return widget->SizeMode() == nsSizeMode_Fullscreen;
4546 return false;
4549 nsCOMPtr<nsPIDOMWindowOuter> window = rootItem->GetWindow();
4550 NS_ENSURE_TRUE(window, mFullscreen.isSome());
4552 return nsGlobalWindowOuter::Cast(window)->Fullscreen();
4555 bool nsGlobalWindowOuter::GetFullscreenOuter() { return Fullscreen(); }
4557 bool nsGlobalWindowOuter::GetFullScreen() {
4558 FORWARD_TO_INNER(GetFullScreen, (), false);
4561 void nsGlobalWindowOuter::EnsureReflowFlushAndPaint() {
4562 NS_ASSERTION(mDocShell,
4563 "EnsureReflowFlushAndPaint() called with no "
4564 "docshell!");
4566 if (!mDocShell) return;
4568 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
4569 if (!presShell) {
4570 return;
4573 // Flush pending reflows.
4574 if (mDoc) {
4575 mDoc->FlushPendingNotifications(FlushType::Layout);
4578 // Unsuppress painting.
4579 presShell->UnsuppressPainting();
4582 // static
4583 void nsGlobalWindowOuter::MakeMessageWithPrincipal(
4584 nsAString& aOutMessage, nsIPrincipal* aSubjectPrincipal, bool aUseHostPort,
4585 const char* aNullMessage, const char* aContentMessage,
4586 const char* aFallbackMessage) {
4587 MOZ_ASSERT(aSubjectPrincipal);
4589 aOutMessage.Truncate();
4591 // Try to get a host from the running principal -- this will do the
4592 // right thing for javascript: and data: documents.
4594 nsAutoCString contentDesc;
4596 if (aSubjectPrincipal->GetIsNullPrincipal()) {
4597 nsContentUtils::GetLocalizedString(
4598 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aNullMessage, aOutMessage);
4599 } else {
4600 auto* addonPolicy = BasePrincipal::Cast(aSubjectPrincipal)->AddonPolicy();
4601 if (addonPolicy) {
4602 nsContentUtils::FormatLocalizedString(
4603 aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
4604 aContentMessage, addonPolicy->Name());
4605 } else {
4606 nsresult rv = NS_ERROR_FAILURE;
4607 if (aUseHostPort) {
4608 nsCOMPtr<nsIURI> uri = aSubjectPrincipal->GetURI();
4609 if (uri) {
4610 rv = uri->GetDisplayHostPort(contentDesc);
4613 if (!aUseHostPort || NS_FAILED(rv)) {
4614 rv = aSubjectPrincipal->GetExposablePrePath(contentDesc);
4616 if (NS_SUCCEEDED(rv) && !contentDesc.IsEmpty()) {
4617 NS_ConvertUTF8toUTF16 ucsPrePath(contentDesc);
4618 nsContentUtils::FormatLocalizedString(
4619 aOutMessage, nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
4620 aContentMessage, ucsPrePath);
4625 if (aOutMessage.IsEmpty()) {
4626 // We didn't find a host so use the generic heading
4627 nsContentUtils::GetLocalizedString(
4628 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, aFallbackMessage,
4629 aOutMessage);
4632 // Just in case
4633 if (aOutMessage.IsEmpty()) {
4634 NS_WARNING(
4635 "could not get ScriptDlgGenericHeading string from string bundle");
4636 aOutMessage.AssignLiteral("[Script]");
4640 bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType) {
4641 // When called from chrome, we can avoid the following checks.
4642 if (aCallerType != CallerType::System) {
4643 // Don't allow scripts to move or resize windows that were not opened by a
4644 // script.
4645 if (!mBrowsingContext->HadOriginalOpener()) {
4646 return false;
4649 if (!CanSetProperty("dom.disable_window_move_resize")) {
4650 return false;
4653 // Ignore the request if we have more than one tab in the window.
4654 if (mBrowsingContext->Top()->HasSiblings()) {
4655 return false;
4659 if (mDocShell) {
4660 bool allow;
4661 nsresult rv = mDocShell->GetAllowWindowControl(&allow);
4662 if (NS_SUCCEEDED(rv) && !allow) return false;
4665 if (nsGlobalWindowInner::sMouseDown &&
4666 !nsGlobalWindowInner::sDragServiceDisabled) {
4667 nsCOMPtr<nsIDragService> ds =
4668 do_GetService("@mozilla.org/widget/dragservice;1");
4669 if (ds) {
4670 nsGlobalWindowInner::sDragServiceDisabled = true;
4671 ds->Suppress();
4674 return true;
4677 bool nsGlobalWindowOuter::AlertOrConfirm(bool aAlert, const nsAString& aMessage,
4678 nsIPrincipal& aSubjectPrincipal,
4679 ErrorResult& aError) {
4680 // XXX This method is very similar to nsGlobalWindowOuter::Prompt, make
4681 // sure any modifications here don't need to happen over there!
4682 if (!AreDialogsEnabled()) {
4683 // Just silently return. In the case of alert(), the return value is
4684 // ignored. In the case of confirm(), returning false is the same thing as
4685 // would happen if the user cancels.
4686 return false;
4689 // Reset popup state while opening a modal dialog, and firing events
4690 // about the dialog, to prevent the current state from being active
4691 // the whole time a modal dialog is open.
4692 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
4694 // Before bringing up the window, unsuppress painting and flush
4695 // pending reflows.
4696 EnsureReflowFlushAndPaint();
4698 nsAutoString title;
4699 MakeMessageWithPrincipal(title, &aSubjectPrincipal, false,
4700 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
4701 "ScriptDlgGenericHeading");
4703 // Remove non-terminating null characters from the
4704 // string. See bug #310037.
4705 nsAutoString final;
4706 nsContentUtils::StripNullChars(aMessage, final);
4707 nsContentUtils::PlatformToDOMLineBreaks(final);
4709 nsresult rv;
4710 nsCOMPtr<nsIPromptFactory> promptFac =
4711 do_GetService("@mozilla.org/prompter;1", &rv);
4712 if (NS_FAILED(rv)) {
4713 aError.Throw(rv);
4714 return false;
4717 nsCOMPtr<nsIPrompt> prompt;
4718 aError =
4719 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
4720 if (aError.Failed()) {
4721 return false;
4724 // Always allow content modal prompts for alert and confirm.
4725 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
4726 promptBag->SetPropertyAsUint32(u"modalType"_ns,
4727 nsIPrompt::MODAL_TYPE_CONTENT);
4730 bool result = false;
4731 nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput);
4732 if (ShouldPromptToBlockDialogs()) {
4733 bool disallowDialog = false;
4734 nsAutoString label;
4735 MakeMessageWithPrincipal(
4736 label, &aSubjectPrincipal, true, "ScriptDialogLabelNullPrincipal",
4737 "ScriptDialogLabelContentPrincipal", "ScriptDialogLabelNullPrincipal");
4739 aError = aAlert
4740 ? prompt->AlertCheck(title.get(), final.get(), label.get(),
4741 &disallowDialog)
4742 : prompt->ConfirmCheck(title.get(), final.get(), label.get(),
4743 &disallowDialog, &result);
4745 if (disallowDialog) {
4746 DisableDialogs();
4748 } else {
4749 aError = aAlert ? prompt->Alert(title.get(), final.get())
4750 : prompt->Confirm(title.get(), final.get(), &result);
4753 return result;
4756 void nsGlobalWindowOuter::AlertOuter(const nsAString& aMessage,
4757 nsIPrincipal& aSubjectPrincipal,
4758 ErrorResult& aError) {
4759 AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
4762 bool nsGlobalWindowOuter::ConfirmOuter(const nsAString& aMessage,
4763 nsIPrincipal& aSubjectPrincipal,
4764 ErrorResult& aError) {
4765 return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
4766 aError);
4769 void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage,
4770 const nsAString& aInitial,
4771 nsAString& aReturn,
4772 nsIPrincipal& aSubjectPrincipal,
4773 ErrorResult& aError) {
4774 // XXX This method is very similar to nsGlobalWindowOuter::AlertOrConfirm,
4775 // make sure any modifications here don't need to happen over there!
4776 SetDOMStringToNull(aReturn);
4778 if (!AreDialogsEnabled()) {
4779 // Return null, as if the user just canceled the prompt.
4780 return;
4783 // Reset popup state while opening a modal dialog, and firing events
4784 // about the dialog, to prevent the current state from being active
4785 // the whole time a modal dialog is open.
4786 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
4788 // Before bringing up the window, unsuppress painting and flush
4789 // pending reflows.
4790 EnsureReflowFlushAndPaint();
4792 nsAutoString title;
4793 MakeMessageWithPrincipal(title, &aSubjectPrincipal, false,
4794 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
4795 "ScriptDlgGenericHeading");
4797 // Remove non-terminating null characters from the
4798 // string. See bug #310037.
4799 nsAutoString fixedMessage, fixedInitial;
4800 nsContentUtils::StripNullChars(aMessage, fixedMessage);
4801 nsContentUtils::PlatformToDOMLineBreaks(fixedMessage);
4802 nsContentUtils::StripNullChars(aInitial, fixedInitial);
4804 nsresult rv;
4805 nsCOMPtr<nsIPromptFactory> promptFac =
4806 do_GetService("@mozilla.org/prompter;1", &rv);
4807 if (NS_FAILED(rv)) {
4808 aError.Throw(rv);
4809 return;
4812 nsCOMPtr<nsIPrompt> prompt;
4813 aError =
4814 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
4815 if (aError.Failed()) {
4816 return;
4819 // Always allow content modal prompts for prompt.
4820 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
4821 promptBag->SetPropertyAsUint32(u"modalType"_ns,
4822 nsIPrompt::MODAL_TYPE_CONTENT);
4825 // Pass in the default value, if any.
4826 char16_t* inoutValue = ToNewUnicode(fixedInitial);
4827 bool disallowDialog = false;
4829 nsAutoString label;
4830 label.SetIsVoid(true);
4831 if (ShouldPromptToBlockDialogs()) {
4832 nsContentUtils::GetLocalizedString(
4833 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label);
4836 nsAutoSyncOperation sync(mDoc, SyncOperationBehavior::eSuspendInput);
4837 bool ok;
4838 aError = prompt->Prompt(title.get(), fixedMessage.get(), &inoutValue,
4839 label.IsVoid() ? nullptr : label.get(),
4840 &disallowDialog, &ok);
4842 if (disallowDialog) {
4843 DisableDialogs();
4846 // XXX Doesn't this leak inoutValue?
4847 if (aError.Failed()) {
4848 return;
4851 nsString outValue;
4852 outValue.Adopt(inoutValue);
4853 if (ok && inoutValue) {
4854 aReturn = std::move(outValue);
4858 void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType,
4859 bool aFromOtherProcess,
4860 uint64_t aActionId) {
4861 RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
4862 if (MOZ_UNLIKELY(!fm)) {
4863 return;
4866 auto [canFocus, isActive] = GetBrowsingContext()->CanFocusCheck(aCallerType);
4867 if (aFromOtherProcess) {
4868 // We trust that the check passed in a process that's, in principle,
4869 // untrusted, because we don't have the required caller context available
4870 // here. Also, the worst that the other process can do in this case is to
4871 // raise a window it's not supposed to be allowed to raise.
4872 // https://bugzilla.mozilla.org/show_bug.cgi?id=1677899
4873 MOZ_ASSERT(XRE_IsContentProcess(),
4874 "Parent should not trust other processes.");
4875 canFocus = true;
4878 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4879 if (treeOwnerAsWin && (canFocus || isActive)) {
4880 bool isEnabled = true;
4881 if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
4882 NS_WARNING("Should not try to set the focus on a disabled window");
4883 return;
4887 if (!mDocShell) {
4888 return;
4891 // If the window has a child frame focused, clear the focus. This
4892 // ensures that focus will be in this frame and not in a child.
4893 if (nsIContent* content = GetFocusedElement()) {
4894 if (HTMLIFrameElement::FromNode(content)) {
4895 fm->ClearFocus(this);
4899 RefPtr<BrowsingContext> parent;
4900 BrowsingContext* bc = GetBrowsingContext();
4901 if (bc) {
4902 parent = bc->GetParent();
4903 if (!parent && XRE_IsParentProcess()) {
4904 parent = bc->Canonical()->GetParentCrossChromeBoundary();
4907 if (parent) {
4908 if (!parent->IsInProcess()) {
4909 if (isActive) {
4910 OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
4911 fm->WindowRaised(kungFuDeathGrip, aActionId);
4912 } else {
4913 ContentChild* contentChild = ContentChild::GetSingleton();
4914 MOZ_ASSERT(contentChild);
4915 contentChild->SendFinalizeFocusOuter(bc, canFocus, aCallerType);
4917 return;
4920 MOZ_ASSERT(mDoc, "Call chain should have ensured document creation.");
4921 if (mDoc) {
4922 if (Element* frame = mDoc->GetEmbedderElement()) {
4923 nsContentUtils::RequestFrameFocus(*frame, canFocus, aCallerType);
4926 return;
4929 if (canFocus) {
4930 // if there is no parent, this must be a toplevel window, so raise the
4931 // window if canFocus is true. If this is a child process, the raise
4932 // window request will get forwarded to the parent by the puppet widget.
4933 OwningNonNull<nsGlobalWindowOuter> kungFuDeathGrip(*this);
4934 fm->RaiseWindow(kungFuDeathGrip, aCallerType, aActionId);
4938 nsresult nsGlobalWindowOuter::Focus(CallerType aCallerType) {
4939 FORWARD_TO_INNER(Focus, (aCallerType), NS_ERROR_UNEXPECTED);
4942 void nsGlobalWindowOuter::BlurOuter(CallerType aCallerType) {
4943 if (!GetBrowsingContext()->CanBlurCheck(aCallerType)) {
4944 return;
4947 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
4948 if (chrome) {
4949 chrome->Blur();
4953 void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) {
4954 // IsNavigationAllowed checks are usually done in nsDocShell directly,
4955 // however nsDocShell::Stop has a bunch of internal users that would fail
4956 // the IsNavigationAllowed check.
4957 if (!mDocShell || !nsDocShell::Cast(mDocShell)->IsNavigationAllowed()) {
4958 return;
4961 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
4962 if (webNav) {
4963 aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
4967 void nsGlobalWindowOuter::PrintOuter(ErrorResult& aError) {
4968 if (!AreDialogsEnabled()) {
4969 // Per spec, silently return. https://github.com/whatwg/html/commit/21a1de1
4970 return;
4973 // Printing is disabled, silently return.
4974 if (!StaticPrefs::print_enabled()) {
4975 return;
4978 // If we're loading, queue the print for later. This is a special-case that
4979 // only applies to the window.print() call, for compat with other engines and
4980 // pre-existing behavior.
4981 if (mShouldDelayPrintUntilAfterLoad) {
4982 if (nsIDocShell* docShell = GetDocShell()) {
4983 if (docShell->GetBusyFlags() & nsIDocShell::BUSY_FLAGS_PAGE_LOADING) {
4984 mDelayedPrintUntilAfterLoad = true;
4985 return;
4990 #ifdef NS_PRINTING
4991 RefPtr<BrowsingContext> top =
4992 mBrowsingContext ? mBrowsingContext->Top() : nullptr;
4993 if (NS_WARN_IF(top && top->GetIsPrinting())) {
4994 return;
4997 if (top) {
4998 Unused << top->SetIsPrinting(true);
5001 auto unset = MakeScopeExit([&] {
5002 if (top) {
5003 Unused << top->SetIsPrinting(false);
5007 const bool forPreview = !StaticPrefs::print_always_print_silent();
5008 Print(nullptr, nullptr, nullptr, nullptr, IsPreview(forPreview),
5009 IsForWindowDotPrint::Yes, nullptr, nullptr, aError);
5010 #endif
5013 class MOZ_RAII AutoModalState {
5014 public:
5015 explicit AutoModalState(nsGlobalWindowOuter& aWin)
5016 : mModalStateWin(aWin.EnterModalState()) {}
5018 ~AutoModalState() {
5019 if (mModalStateWin) {
5020 mModalStateWin->LeaveModalState();
5024 RefPtr<nsGlobalWindowOuter> mModalStateWin;
5027 Nullable<WindowProxyHolder> nsGlobalWindowOuter::Print(
5028 nsIPrintSettings* aPrintSettings, RemotePrintJobChild* aRemotePrintJob,
5029 nsIWebProgressListener* aListener, nsIDocShell* aDocShellToCloneInto,
5030 IsPreview aIsPreview, IsForWindowDotPrint aForWindowDotPrint,
5031 PrintPreviewResolver&& aPrintPreviewCallback,
5032 RefPtr<BrowsingContext>* aCachedBrowsingContext, ErrorResult& aError) {
5033 #ifdef NS_PRINTING
5034 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
5035 do_GetService("@mozilla.org/gfx/printsettings-service;1");
5036 if (!printSettingsService) {
5037 // we currently return here in headless mode - should we?
5038 aError.ThrowNotSupportedError("No print settings service");
5039 return nullptr;
5042 nsCOMPtr<nsIPrintSettings> ps = aPrintSettings;
5043 if (!ps) {
5044 // We shouldn't need this once bug 1776169 is fixed.
5045 printSettingsService->GetDefaultPrintSettingsForPrinting(
5046 getter_AddRefs(ps));
5049 RefPtr<Document> docToPrint = mDoc;
5050 if (NS_WARN_IF(!docToPrint)) {
5051 aError.ThrowNotSupportedError("Document is gone");
5052 return nullptr;
5055 RefPtr<BrowsingContext> sourceBC = docToPrint->GetBrowsingContext();
5056 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
5057 if (!sourceBC) {
5058 aError.ThrowNotSupportedError("No browsing context for source document");
5059 return nullptr;
5062 nsAutoSyncOperation sync(docToPrint, SyncOperationBehavior::eAllowInput);
5063 AutoModalState modalState(*this);
5065 nsCOMPtr<nsIDocumentViewer> viewer;
5066 RefPtr<BrowsingContext> bc;
5067 bool hasPrintCallbacks = false;
5068 bool wasStaticDocument = docToPrint->IsStaticDocument();
5069 bool usingCachedBrowsingContext = false;
5070 if (aCachedBrowsingContext && *aCachedBrowsingContext) {
5071 MOZ_ASSERT(!wasStaticDocument,
5072 "Why pass in non-empty aCachedBrowsingContext if original "
5073 "document is already static?");
5074 if (!wasStaticDocument) {
5075 // The passed in document is not a static clone and the caller passed in a
5076 // static clone to reuse, so swap it in.
5077 docToPrint = (*aCachedBrowsingContext)->GetDocument();
5078 MOZ_ASSERT(docToPrint);
5079 MOZ_ASSERT(docToPrint->IsStaticDocument());
5080 wasStaticDocument = true;
5081 usingCachedBrowsingContext = true;
5084 if (wasStaticDocument) {
5085 if (aForWindowDotPrint == IsForWindowDotPrint::Yes) {
5086 aError.ThrowNotSupportedError(
5087 "Calling print() from a print preview is unsupported, did you intend "
5088 "to call printPreview() instead?");
5089 return nullptr;
5091 if (usingCachedBrowsingContext) {
5092 bc = docToPrint->GetBrowsingContext();
5093 } else {
5094 // We're already a print preview window, just reuse our browsing context /
5095 // content viewer.
5096 bc = sourceBC;
5098 nsCOMPtr<nsIDocShell> docShell = bc->GetDocShell();
5099 if (!docShell) {
5100 aError.ThrowNotSupportedError("No docshell");
5101 return nullptr;
5103 // We could handle this if needed.
5104 if (aDocShellToCloneInto && aDocShellToCloneInto != docShell) {
5105 aError.ThrowNotSupportedError(
5106 "We don't handle cloning a print preview doc into a different "
5107 "docshell");
5108 return nullptr;
5110 docShell->GetDocViewer(getter_AddRefs(viewer));
5111 MOZ_DIAGNOSTIC_ASSERT(viewer);
5112 } else {
5113 if (aDocShellToCloneInto) {
5114 // Ensure the content viewer is created if needed.
5115 Unused << aDocShellToCloneInto->GetDocument();
5116 bc = aDocShellToCloneInto->GetBrowsingContext();
5117 } else {
5118 AutoNoJSAPI nojsapi;
5119 auto printKind = aForWindowDotPrint == IsForWindowDotPrint::Yes
5120 ? PrintKind::WindowDotPrint
5121 : PrintKind::InternalPrint;
5122 // For PrintKind::WindowDotPrint, this call will not only make the parent
5123 // process create a CanonicalBrowsingContext for the returned `bc`, but
5124 // it will also make the parent process initiate the print/print preview.
5125 // See the handling of OPEN_PRINT_BROWSER in browser.js.
5126 aError = OpenInternal(u""_ns, u""_ns, u""_ns,
5127 false, // aDialog
5128 false, // aContentModal
5129 true, // aCalledNoScript
5130 false, // aDoJSFixups
5131 true, // aNavigate
5132 nullptr, nullptr, // No args
5133 nullptr, // aLoadState
5134 false, // aForceNoOpener
5135 printKind, getter_AddRefs(bc));
5136 if (NS_WARN_IF(aError.Failed())) {
5137 return nullptr;
5139 if (aCachedBrowsingContext) {
5140 MOZ_ASSERT(!*aCachedBrowsingContext);
5141 *aCachedBrowsingContext = bc;
5144 if (!bc) {
5145 aError.ThrowNotAllowedError("No browsing context");
5146 return nullptr;
5149 Unused << bc->Top()->SetIsPrinting(true);
5150 nsCOMPtr<nsIDocShell> cloneDocShell = bc->GetDocShell();
5151 MOZ_DIAGNOSTIC_ASSERT(cloneDocShell);
5152 cloneDocShell->GetDocViewer(getter_AddRefs(viewer));
5153 MOZ_DIAGNOSTIC_ASSERT(viewer);
5154 if (!viewer) {
5155 aError.ThrowNotSupportedError("Didn't end up with a content viewer");
5156 return nullptr;
5159 if (bc != sourceBC) {
5160 MOZ_ASSERT(bc->IsTopContent());
5161 // If we are cloning from a document in a different BrowsingContext, we
5162 // need to make sure to copy over our opener policy information from that
5163 // BrowsingContext. In the case where the source is an iframe, this
5164 // information needs to be copied from the toplevel source
5165 // BrowsingContext, as we may be making a static clone of a single
5166 // subframe.
5167 MOZ_ALWAYS_SUCCEEDS(
5168 bc->SetOpenerPolicy(sourceBC->Top()->GetOpenerPolicy()));
5169 MOZ_DIAGNOSTIC_ASSERT(bc->Group() == sourceBC->Group());
5172 if (RefPtr<Document> doc = viewer->GetDocument()) {
5173 if (doc->IsShowing()) {
5174 // We're going to drop this document on the floor, in the SetDocument
5175 // call below. Make sure to run OnPageHide() to keep state consistent
5176 // and avoids assertions in the document destructor.
5177 doc->OnPageHide(false, nullptr);
5181 AutoPrintEventDispatcher dispatcher(*docToPrint);
5183 nsAutoScriptBlocker blockScripts;
5184 RefPtr<Document> clone = docToPrint->CreateStaticClone(
5185 cloneDocShell, viewer, ps, &hasPrintCallbacks);
5186 if (!clone) {
5187 aError.ThrowNotSupportedError("Clone operation for printing failed");
5188 return nullptr;
5192 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_QueryInterface(viewer);
5193 if (!webBrowserPrint) {
5194 aError.ThrowNotSupportedError(
5195 "Content viewer didn't implement nsIWebBrowserPrint");
5196 return nullptr;
5198 bool closeWindowAfterPrint;
5199 if (wasStaticDocument) {
5200 // Here the document was a static clone to begin with that this code did not
5201 // create, so we should not clean it up.
5202 // The exception is if we're using the passed-in aCachedBrowsingContext, in
5203 // which case this is the second print with this static document clone that
5204 // we created the first time through, and we are responsible for cleaning it
5205 // up.
5206 closeWindowAfterPrint = usingCachedBrowsingContext;
5207 } else {
5208 // In this case the document was not a static clone, so we made a static
5209 // clone for printing purposes and must clean it up after the print is done.
5210 // The exception is if aCachedBrowsingContext is non-NULL, meaning the
5211 // caller is intending to print this document again, so we need to defer the
5212 // cleanup until after the second print.
5213 closeWindowAfterPrint = !aCachedBrowsingContext;
5215 webBrowserPrint->SetCloseWindowAfterPrint(closeWindowAfterPrint);
5217 // For window.print(), we postpone making these calls until the round-trip to
5218 // the parent process (triggered by the OpenInternal call above) calls us
5219 // again. Only a call from the parent can provide a valid nsPrintSettings
5220 // object and RemotePrintJobChild object.
5221 if (aForWindowDotPrint == IsForWindowDotPrint::No) {
5222 if (aIsPreview == IsPreview::Yes) {
5223 aError = webBrowserPrint->PrintPreview(ps, aListener,
5224 std::move(aPrintPreviewCallback));
5225 if (aError.Failed()) {
5226 return nullptr;
5228 } else {
5229 // Historically we've eaten this error.
5230 webBrowserPrint->Print(ps, aRemotePrintJob, aListener);
5234 // When using window.print() with the new UI, we usually want to block until
5235 // the print dialog is hidden. But we can't really do that if we have print
5236 // callbacks, because we are inside a sync operation, and we want to run
5237 // microtasks / etc that the print callbacks may create. It is really awkward
5238 // to have this subtle behavior difference...
5240 // We also want to do this for fuzzing, so that they can test window.print().
5241 const bool shouldBlock = [&] {
5242 if (aForWindowDotPrint == IsForWindowDotPrint::No) {
5243 return false;
5245 if (aIsPreview == IsPreview::Yes) {
5246 return !hasPrintCallbacks;
5248 return StaticPrefs::dom_window_print_fuzzing_block_while_printing();
5249 }();
5251 if (shouldBlock) {
5252 SpinEventLoopUntil("nsGlobalWindowOuter::Print"_ns,
5253 [&] { return bc->IsDiscarded(); });
5256 return WindowProxyHolder(std::move(bc));
5257 #else
5258 return nullptr;
5259 #endif // NS_PRINTING
5262 void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos, int32_t aYPos,
5263 CallerType aCallerType,
5264 ErrorResult& aError) {
5266 * If caller is not chrome and the user has not explicitly exempted the site,
5267 * prevent window.moveTo() by exiting early
5270 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsSubframe()) {
5271 return;
5274 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5275 if (!treeOwnerAsWin) {
5276 aError.Throw(NS_ERROR_FAILURE);
5277 return;
5280 // We need to do the same transformation GetScreenXY does.
5281 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
5282 if (!presContext) {
5283 return;
5286 CSSIntPoint cssPos(aXPos, aYPos);
5287 CheckSecurityLeftAndTop(&cssPos.x.value, &cssPos.y.value, aCallerType);
5289 nsDeviceContext* context = presContext->DeviceContext();
5291 auto devPos = LayoutDeviceIntPoint::FromAppUnitsRounded(
5292 CSSIntPoint::ToAppUnits(cssPos), context->AppUnitsPerDevPixel());
5294 aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
5295 CheckForDPIChange();
5298 void nsGlobalWindowOuter::MoveByOuter(int32_t aXDif, int32_t aYDif,
5299 CallerType aCallerType,
5300 ErrorResult& aError) {
5302 * If caller is not chrome and the user has not explicitly exempted the site,
5303 * prevent window.moveBy() by exiting early
5306 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsSubframe()) {
5307 return;
5310 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5311 if (!treeOwnerAsWin) {
5312 aError.Throw(NS_ERROR_FAILURE);
5313 return;
5316 // To do this correctly we have to convert what we get from GetPosition
5317 // into CSS pixels, add the arguments, do the security check, and
5318 // then convert back to device pixels for the call to SetPosition.
5320 int32_t x, y;
5321 aError = treeOwnerAsWin->GetPosition(&x, &y);
5322 if (aError.Failed()) {
5323 return;
5326 auto cssScale = CSSToDevScaleForBaseWindow(treeOwnerAsWin);
5327 CSSIntPoint cssPos = RoundedToInt(treeOwnerAsWin->GetPosition() / cssScale);
5329 cssPos.x += aXDif;
5330 cssPos.y += aYDif;
5332 CheckSecurityLeftAndTop(&cssPos.x.value, &cssPos.y.value, aCallerType);
5334 LayoutDeviceIntPoint newDevPos = RoundedToInt(cssPos * cssScale);
5335 aError = treeOwnerAsWin->SetPosition(newDevPos.x, newDevPos.y);
5337 CheckForDPIChange();
5340 nsresult nsGlobalWindowOuter::MoveBy(int32_t aXDif, int32_t aYDif) {
5341 ErrorResult rv;
5342 MoveByOuter(aXDif, aYDif, CallerType::System, rv);
5344 return rv.StealNSResult();
5347 void nsGlobalWindowOuter::ResizeToOuter(int32_t aWidth, int32_t aHeight,
5348 CallerType aCallerType,
5349 ErrorResult& aError) {
5351 * If caller is not chrome and the user has not explicitly exempted the site,
5352 * prevent window.resizeTo() by exiting early
5355 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsSubframe()) {
5356 return;
5359 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5360 if (!treeOwnerAsWin) {
5361 aError.Throw(NS_ERROR_FAILURE);
5362 return;
5365 CSSIntSize cssSize(aWidth, aHeight);
5366 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5368 LayoutDeviceIntSize devSize =
5369 RoundedToInt(cssSize * CSSToDevScaleForBaseWindow(treeOwnerAsWin));
5370 aError = treeOwnerAsWin->SetSize(devSize.width, devSize.height, true);
5372 CheckForDPIChange();
5375 void nsGlobalWindowOuter::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif,
5376 CallerType aCallerType,
5377 ErrorResult& aError) {
5379 * If caller is not chrome and the user has not explicitly exempted the site,
5380 * prevent window.resizeBy() by exiting early
5383 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsSubframe()) {
5384 return;
5387 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5388 if (!treeOwnerAsWin) {
5389 aError.Throw(NS_ERROR_FAILURE);
5390 return;
5393 LayoutDeviceIntSize size = treeOwnerAsWin->GetSize();
5395 // To do this correctly we have to convert what we got from GetSize
5396 // into CSS pixels, add the arguments, do the security check, and
5397 // then convert back to device pixels for the call to SetSize.
5399 auto scale = CSSToDevScaleForBaseWindow(treeOwnerAsWin);
5400 CSSIntSize cssSize = RoundedToInt(size / scale);
5402 cssSize.width += aWidthDif;
5403 cssSize.height += aHeightDif;
5405 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5407 LayoutDeviceIntSize newDevSize = RoundedToInt(cssSize * scale);
5409 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
5411 CheckForDPIChange();
5414 void nsGlobalWindowOuter::SizeToContentOuter(
5415 CallerType aCallerType, const SizeToContentConstraints& aConstraints,
5416 ErrorResult& aError) {
5417 if (!mDocShell) {
5418 return;
5422 * If caller is not chrome and the user has not explicitly exempted the site,
5423 * prevent window.sizeToContent() by exiting early
5426 if (!CanMoveResizeWindows(aCallerType) || mBrowsingContext->IsSubframe()) {
5427 return;
5430 // The content viewer does a check to make sure that it's a content
5431 // viewer for a toplevel docshell.
5432 nsCOMPtr<nsIDocumentViewer> viewer;
5433 mDocShell->GetDocViewer(getter_AddRefs(viewer));
5434 if (!viewer) {
5435 return aError.Throw(NS_ERROR_FAILURE);
5438 auto contentSize = viewer->GetContentSize(
5439 aConstraints.mMaxWidth, aConstraints.mMaxHeight, aConstraints.mPrefWidth);
5440 if (!contentSize) {
5441 return aError.Throw(NS_ERROR_FAILURE);
5444 // Make sure the new size is following the CheckSecurityWidthAndHeight
5445 // rules.
5446 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
5447 if (!treeOwner) {
5448 return aError.Throw(NS_ERROR_FAILURE);
5451 // Don't use DevToCSSIntPixelsForBaseWindow() nor
5452 // CSSToDevIntPixelsForBaseWindow() here because contentSize is comes from
5453 // nsIDocumentViewer::GetContentSize() and it's computed with nsPresContext so
5454 // that we need to work with nsPresContext here too.
5455 RefPtr<nsPresContext> presContext = viewer->GetPresContext();
5456 MOZ_ASSERT(
5457 presContext,
5458 "Should be non-nullptr if nsIDocumentViewer::GetContentSize() succeeded");
5459 CSSIntSize cssSize = *contentSize;
5460 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5462 LayoutDeviceIntSize newDevSize(
5463 presContext->CSSPixelsToDevPixels(cssSize.width),
5464 presContext->CSSPixelsToDevPixels(cssSize.height));
5466 nsCOMPtr<nsIDocShell> docShell = mDocShell;
5467 aError =
5468 treeOwner->SizeShellTo(docShell, newDevSize.width, newDevSize.height);
5471 already_AddRefed<nsPIWindowRoot> nsGlobalWindowOuter::GetTopWindowRoot() {
5472 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
5473 if (!piWin) {
5474 return nullptr;
5477 nsCOMPtr<nsPIWindowRoot> window =
5478 do_QueryInterface(piWin->GetChromeEventHandler());
5479 return window.forget();
5482 void nsGlobalWindowOuter::FirePopupBlockedEvent(
5483 Document* aDoc, nsIURI* aPopupURI, const nsAString& aPopupWindowName,
5484 const nsAString& aPopupWindowFeatures) {
5485 MOZ_ASSERT(aDoc);
5487 // Fire a "DOMPopupBlocked" event so that the UI can hear about
5488 // blocked popups.
5489 PopupBlockedEventInit init;
5490 init.mBubbles = true;
5491 init.mCancelable = true;
5492 // XXX: This is a different object, but webidl requires an inner window here
5493 // now.
5494 init.mRequestingWindow = GetCurrentInnerWindowInternal(this);
5495 init.mPopupWindowURI = aPopupURI;
5496 init.mPopupWindowName = aPopupWindowName;
5497 init.mPopupWindowFeatures = aPopupWindowFeatures;
5499 RefPtr<PopupBlockedEvent> event =
5500 PopupBlockedEvent::Constructor(aDoc, u"DOMPopupBlocked"_ns, init);
5502 event->SetTrusted(true);
5504 aDoc->DispatchEvent(*event);
5507 // static
5508 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName) {
5509 // Chrome can set any property.
5510 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
5511 return true;
5514 // If the pref is set to true, we can not set the property
5515 // and vice versa.
5516 return !Preferences::GetBool(aPrefName, true);
5519 /* If a window open is blocked, fire the appropriate DOM events. */
5520 void nsGlobalWindowOuter::FireAbuseEvents(
5521 const nsAString& aPopupURL, const nsAString& aPopupWindowName,
5522 const nsAString& aPopupWindowFeatures) {
5523 // fetch the URI of the window requesting the opened window
5524 nsCOMPtr<Document> currentDoc = GetDoc();
5525 nsCOMPtr<nsIURI> popupURI;
5527 // build the URI of the would-have-been popup window
5528 // (see nsWindowWatcher::URIfromURL)
5530 // first, fetch the opener's base URI
5532 nsIURI* baseURL = nullptr;
5534 nsCOMPtr<Document> doc = GetEntryDocument();
5535 if (doc) baseURL = doc->GetDocBaseURI();
5537 // use the base URI to build what would have been the popup's URI
5538 nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
5539 if (ios)
5540 ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), nullptr, baseURL,
5541 getter_AddRefs(popupURI));
5543 // fire an event block full of informative URIs
5544 FirePopupBlockedEvent(currentDoc, popupURI, aPopupWindowName,
5545 aPopupWindowFeatures);
5548 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenOuter(
5549 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
5550 ErrorResult& aError) {
5551 RefPtr<BrowsingContext> bc;
5552 nsresult rv = OpenJS(aUrl, aName, aOptions, getter_AddRefs(bc));
5553 if (rv == NS_ERROR_MALFORMED_URI) {
5554 aError.ThrowSyntaxError("Unable to open a window with invalid URL '"_ns +
5555 NS_ConvertUTF16toUTF8(aUrl) + "'."_ns);
5556 return nullptr;
5559 // XXX Is it possible that some internal errors are thrown here?
5560 aError = rv;
5562 if (!bc) {
5563 return nullptr;
5565 return WindowProxyHolder(std::move(bc));
5568 nsresult nsGlobalWindowOuter::Open(const nsAString& aUrl,
5569 const nsAString& aName,
5570 const nsAString& aOptions,
5571 nsDocShellLoadState* aLoadState,
5572 bool aForceNoOpener,
5573 BrowsingContext** _retval) {
5574 return OpenInternal(aUrl, aName, aOptions,
5575 false, // aDialog
5576 false, // aContentModal
5577 true, // aCalledNoScript
5578 false, // aDoJSFixups
5579 true, // aNavigate
5580 nullptr, nullptr, // No args
5581 aLoadState, aForceNoOpener, PrintKind::None, _retval);
5584 nsresult nsGlobalWindowOuter::OpenJS(const nsAString& aUrl,
5585 const nsAString& aName,
5586 const nsAString& aOptions,
5587 BrowsingContext** _retval) {
5588 return OpenInternal(aUrl, aName, aOptions,
5589 false, // aDialog
5590 false, // aContentModal
5591 false, // aCalledNoScript
5592 true, // aDoJSFixups
5593 true, // aNavigate
5594 nullptr, nullptr, // No args
5595 nullptr, // aLoadState
5596 false, // aForceNoOpener
5597 PrintKind::None, _retval);
5600 // like Open, but attaches to the new window any extra parameters past
5601 // [features] as a JS property named "arguments"
5602 nsresult nsGlobalWindowOuter::OpenDialog(const nsAString& aUrl,
5603 const nsAString& aName,
5604 const nsAString& aOptions,
5605 nsISupports* aExtraArgument,
5606 BrowsingContext** _retval) {
5607 return OpenInternal(aUrl, aName, aOptions,
5608 true, // aDialog
5609 false, // aContentModal
5610 true, // aCalledNoScript
5611 false, // aDoJSFixups
5612 true, // aNavigate
5613 nullptr, aExtraArgument, // Arguments
5614 nullptr, // aLoadState
5615 false, // aForceNoOpener
5616 PrintKind::None, _retval);
5619 // Like Open, but passes aNavigate=false.
5620 /* virtual */
5621 nsresult nsGlobalWindowOuter::OpenNoNavigate(const nsAString& aUrl,
5622 const nsAString& aName,
5623 const nsAString& aOptions,
5624 BrowsingContext** _retval) {
5625 return OpenInternal(aUrl, aName, aOptions,
5626 false, // aDialog
5627 false, // aContentModal
5628 true, // aCalledNoScript
5629 false, // aDoJSFixups
5630 false, // aNavigate
5631 nullptr, nullptr, // No args
5632 nullptr, // aLoadState
5633 false, // aForceNoOpener
5634 PrintKind::None, _retval);
5637 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenDialogOuter(
5638 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
5639 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
5640 ErrorResult& aError) {
5641 nsCOMPtr<nsIJSArgArray> argvArray;
5642 aError =
5643 NS_CreateJSArgv(aCx, aExtraArgument.Length(), aExtraArgument.Elements(),
5644 getter_AddRefs(argvArray));
5645 if (aError.Failed()) {
5646 return nullptr;
5649 RefPtr<BrowsingContext> dialog;
5650 aError = OpenInternal(aUrl, aName, aOptions,
5651 true, // aDialog
5652 false, // aContentModal
5653 false, // aCalledNoScript
5654 false, // aDoJSFixups
5655 true, // aNavigate
5656 argvArray, nullptr, // Arguments
5657 nullptr, // aLoadState
5658 false, // aForceNoOpener
5659 PrintKind::None, getter_AddRefs(dialog));
5660 if (!dialog) {
5661 return nullptr;
5663 return WindowProxyHolder(std::move(dialog));
5666 WindowProxyHolder nsGlobalWindowOuter::GetFramesOuter() {
5667 RefPtr<nsPIDOMWindowOuter> frames(this);
5668 FlushPendingNotifications(FlushType::ContentAndNotify);
5669 return WindowProxyHolder(mBrowsingContext);
5672 /* static */
5673 bool nsGlobalWindowOuter::GatherPostMessageData(
5674 JSContext* aCx, const nsAString& aTargetOrigin, BrowsingContext** aSource,
5675 nsAString& aOrigin, nsIURI** aTargetOriginURI,
5676 nsIPrincipal** aCallerPrincipal, nsGlobalWindowInner** aCallerInnerWindow,
5677 nsIURI** aCallerURI, Maybe<nsID>* aCallerAgentClusterId,
5678 nsACString* aScriptLocation, ErrorResult& aError) {
5680 // Window.postMessage is an intentional subversion of the same-origin policy.
5681 // As such, this code must be particularly careful in the information it
5682 // exposes to calling code.
5684 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5687 // First, get the caller's window
5688 RefPtr<nsGlobalWindowInner> callerInnerWin =
5689 nsContentUtils::IncumbentInnerWindow();
5690 nsIPrincipal* callerPrin;
5691 if (callerInnerWin) {
5692 RefPtr<Document> doc = callerInnerWin->GetExtantDoc();
5693 if (!doc) {
5694 return false;
5696 NS_IF_ADDREF(*aCallerURI = doc->GetDocumentURI());
5698 // Compute the caller's origin either from its principal or, in the case the
5699 // principal doesn't carry a URI (e.g. the system principal), the caller's
5700 // document. We must get this now instead of when the event is created and
5701 // dispatched, because ultimately it is the identity of the calling window
5702 // *now* that determines who sent the message (and not an identity which
5703 // might have changed due to intervening navigations).
5704 callerPrin = callerInnerWin->GetPrincipal();
5705 } else {
5706 // In case the global is not a window, it can be a sandbox, and the
5707 // sandbox's principal can be used for the security check.
5708 nsIGlobalObject* global = GetIncumbentGlobal();
5709 NS_ASSERTION(global, "Why is there no global object?");
5710 callerPrin = global->PrincipalOrNull();
5711 if (callerPrin) {
5712 BasePrincipal::Cast(callerPrin)->GetScriptLocation(*aScriptLocation);
5715 if (!callerPrin) {
5716 return false;
5719 // if the principal has a URI, use that to generate the origin
5720 if (!callerPrin->IsSystemPrincipal()) {
5721 nsAutoCString webExposedOriginSerialization;
5722 callerPrin->GetWebExposedOriginSerialization(webExposedOriginSerialization);
5723 CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
5724 } else if (callerInnerWin) {
5725 if (!*aCallerURI) {
5726 return false;
5728 // otherwise use the URI of the document to generate origin
5729 nsContentUtils::GetWebExposedOriginSerialization(*aCallerURI, aOrigin);
5730 } else {
5731 // in case of a sandbox with a system principal origin can be empty
5732 if (!callerPrin->IsSystemPrincipal()) {
5733 return false;
5736 NS_IF_ADDREF(*aCallerPrincipal = callerPrin);
5738 // "/" indicates same origin as caller, "*" indicates no specific origin is
5739 // required.
5740 if (!aTargetOrigin.EqualsASCII("/") && !aTargetOrigin.EqualsASCII("*")) {
5741 nsCOMPtr<nsIURI> targetOriginURI;
5742 if (NS_FAILED(NS_NewURI(getter_AddRefs(targetOriginURI), aTargetOrigin))) {
5743 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5744 return false;
5747 nsresult rv = NS_MutateURI(targetOriginURI)
5748 .SetUserPass(""_ns)
5749 .SetPathQueryRef(""_ns)
5750 .Finalize(aTargetOriginURI);
5751 if (NS_FAILED(rv)) {
5752 return false;
5756 if (!nsContentUtils::IsCallerChrome() && callerInnerWin &&
5757 callerInnerWin->GetOuterWindowInternal()) {
5758 NS_ADDREF(*aSource = callerInnerWin->GetOuterWindowInternal()
5759 ->GetBrowsingContext());
5760 } else {
5761 *aSource = nullptr;
5764 if (aCallerAgentClusterId && callerInnerWin &&
5765 callerInnerWin->GetDocGroup()) {
5766 *aCallerAgentClusterId =
5767 Some(callerInnerWin->GetDocGroup()->AgentClusterId());
5770 callerInnerWin.forget(aCallerInnerWindow);
5772 return true;
5775 bool nsGlobalWindowOuter::GetPrincipalForPostMessage(
5776 const nsAString& aTargetOrigin, nsIURI* aTargetOriginURI,
5777 nsIPrincipal* aCallerPrincipal, nsIPrincipal& aSubjectPrincipal,
5778 nsIPrincipal** aProvidedPrincipal) {
5780 // Window.postMessage is an intentional subversion of the same-origin policy.
5781 // As such, this code must be particularly careful in the information it
5782 // exposes to calling code.
5784 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5787 // Convert the provided origin string into a URI for comparison purposes.
5788 nsCOMPtr<nsIPrincipal> providedPrincipal;
5790 if (aTargetOrigin.EqualsASCII("/")) {
5791 providedPrincipal = aCallerPrincipal;
5793 // "*" indicates no specific origin is required.
5794 else if (!aTargetOrigin.EqualsASCII("*")) {
5795 OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef();
5796 if (aSubjectPrincipal.IsSystemPrincipal()) {
5797 auto principal = BasePrincipal::Cast(GetPrincipal());
5799 if (attrs != principal->OriginAttributesRef()) {
5800 nsAutoCString targetURL;
5801 nsAutoCString sourceOrigin;
5802 nsAutoCString targetOrigin;
5804 if (NS_FAILED(principal->GetAsciiSpec(targetURL)) ||
5805 NS_FAILED(principal->GetOrigin(targetOrigin)) ||
5806 NS_FAILED(aSubjectPrincipal.GetOrigin(sourceOrigin))) {
5807 NS_WARNING("Failed to get source and target origins");
5808 return false;
5811 nsContentUtils::LogSimpleConsoleError(
5812 NS_ConvertUTF8toUTF16(nsPrintfCString(
5813 R"(Attempting to post a message to window with url "%s" and )"
5814 R"(origin "%s" from a system principal scope with mismatched )"
5815 R"(origin "%s".)",
5816 targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
5817 "DOM"_ns, !!principal->PrivateBrowsingId(),
5818 principal->IsSystemPrincipal());
5820 attrs = principal->OriginAttributesRef();
5824 // Create a nsIPrincipal inheriting the app/browser attributes from the
5825 // caller.
5826 providedPrincipal =
5827 BasePrincipal::CreateContentPrincipal(aTargetOriginURI, attrs);
5828 if (NS_WARN_IF(!providedPrincipal)) {
5829 return false;
5831 } else {
5832 // We still need to check the originAttributes if the target origin is '*'.
5833 // But we will ingore the FPD here since the FPDs are possible to be
5834 // different.
5835 auto principal = BasePrincipal::Cast(GetPrincipal());
5836 NS_ENSURE_TRUE(principal, false);
5838 OriginAttributes targetAttrs = principal->OriginAttributesRef();
5839 OriginAttributes sourceAttrs = aSubjectPrincipal.OriginAttributesRef();
5840 // We have to exempt the check of OA if the subject prioncipal is a system
5841 // principal since there are many tests try to post messages to content from
5842 // chrome with a mismatch OA. For example, using the ContentTask.spawn() to
5843 // post a message into a private browsing window. The injected code in
5844 // ContentTask.spawn() will be executed under the system principal and the
5845 // OA of the system principal mismatches with the OA of a private browsing
5846 // window.
5847 MOZ_DIAGNOSTIC_ASSERT(aSubjectPrincipal.IsSystemPrincipal() ||
5848 sourceAttrs.EqualsIgnoringFPD(targetAttrs));
5850 // If 'privacy.firstparty.isolate.block_post_message' is true, we will block
5851 // postMessage across different first party domains.
5852 if (OriginAttributes::IsBlockPostMessageForFPI() &&
5853 !aSubjectPrincipal.IsSystemPrincipal() &&
5854 sourceAttrs.mFirstPartyDomain != targetAttrs.mFirstPartyDomain) {
5855 return false;
5859 providedPrincipal.forget(aProvidedPrincipal);
5860 return true;
5863 void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
5864 JS::Handle<JS::Value> aMessage,
5865 const nsAString& aTargetOrigin,
5866 JS::Handle<JS::Value> aTransfer,
5867 nsIPrincipal& aSubjectPrincipal,
5868 ErrorResult& aError) {
5869 RefPtr<BrowsingContext> sourceBc;
5870 nsAutoString origin;
5871 nsCOMPtr<nsIURI> targetOriginURI;
5872 nsCOMPtr<nsIPrincipal> callerPrincipal;
5873 RefPtr<nsGlobalWindowInner> callerInnerWindow;
5874 nsCOMPtr<nsIURI> callerURI;
5875 Maybe<nsID> callerAgentClusterId = Nothing();
5876 nsAutoCString scriptLocation;
5877 if (!GatherPostMessageData(
5878 aCx, aTargetOrigin, getter_AddRefs(sourceBc), origin,
5879 getter_AddRefs(targetOriginURI), getter_AddRefs(callerPrincipal),
5880 getter_AddRefs(callerInnerWindow), getter_AddRefs(callerURI),
5881 &callerAgentClusterId, &scriptLocation, aError)) {
5882 return;
5885 nsCOMPtr<nsIPrincipal> providedPrincipal;
5886 if (!GetPrincipalForPostMessage(aTargetOrigin, targetOriginURI,
5887 callerPrincipal, aSubjectPrincipal,
5888 getter_AddRefs(providedPrincipal))) {
5889 return;
5892 // Create and asynchronously dispatch a runnable which will handle actual DOM
5893 // event creation and dispatch.
5894 RefPtr<PostMessageEvent> event = new PostMessageEvent(
5895 sourceBc, origin, this, providedPrincipal,
5896 callerInnerWindow ? callerInnerWindow->WindowID() : 0, callerURI,
5897 scriptLocation, callerAgentClusterId);
5899 JS::CloneDataPolicy clonePolicy;
5901 if (GetDocGroup() && callerAgentClusterId.isSome() &&
5902 GetDocGroup()->AgentClusterId().Equals(callerAgentClusterId.value())) {
5903 clonePolicy.allowIntraClusterClonableSharedObjects();
5906 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
5907 clonePolicy.allowSharedMemoryObjects();
5910 event->Write(aCx, aMessage, aTransfer, clonePolicy, aError);
5911 if (NS_WARN_IF(aError.Failed())) {
5912 return;
5915 event->DispatchToTargetThread(aError);
5918 class nsCloseEvent : public Runnable {
5919 RefPtr<nsGlobalWindowOuter> mWindow;
5920 bool mIndirect;
5922 nsCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect)
5923 : mozilla::Runnable("nsCloseEvent"),
5924 mWindow(aWindow),
5925 mIndirect(aIndirect) {}
5927 public:
5928 static nsresult PostCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect) {
5929 nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
5930 return aWindow->Dispatch(ev.forget());
5933 NS_IMETHOD Run() override {
5934 if (mWindow) {
5935 if (mIndirect) {
5936 return PostCloseEvent(mWindow, false);
5938 mWindow->ReallyCloseWindow();
5940 return NS_OK;
5944 bool nsGlobalWindowOuter::CanClose() {
5945 if (mIsChrome) {
5946 nsCOMPtr<nsIBrowserDOMWindow> bwin = GetBrowserDOMWindow();
5948 bool canClose = true;
5949 if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
5950 return canClose;
5954 if (!mDocShell) {
5955 return true;
5958 nsCOMPtr<nsIDocumentViewer> viewer;
5959 mDocShell->GetDocViewer(getter_AddRefs(viewer));
5960 if (viewer) {
5961 bool canClose;
5962 nsresult rv = viewer->PermitUnload(&canClose);
5963 if (NS_SUCCEEDED(rv) && !canClose) return false;
5966 // If we still have to print, we delay the closing until print has happened.
5967 if (mShouldDelayPrintUntilAfterLoad && mDelayedPrintUntilAfterLoad) {
5968 mDelayedCloseForPrinting = true;
5969 return false;
5972 return true;
5975 void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) {
5976 if (!mDocShell || IsInModalState() || mBrowsingContext->IsSubframe()) {
5977 // window.close() is called on a frame in a frameset, on a window
5978 // that's already closed, or on a window for which there's
5979 // currently a modal dialog open. Ignore such calls.
5980 return;
5983 if (mHavePendingClose) {
5984 // We're going to be closed anyway; do nothing since we don't want
5985 // to double-close
5986 return;
5989 if (mBlockScriptedClosingFlag) {
5990 // A script's popup has been blocked and we don't want
5991 // the window to be closed directly after this event,
5992 // so the user can see that there was a blocked popup.
5993 return;
5996 // Don't allow scripts from content to close non-neterror windows that
5997 // were not opened by script.
5998 if (mDoc) {
5999 nsAutoString url;
6000 nsresult rv = mDoc->GetURL(url);
6001 NS_ENSURE_SUCCESS_VOID(rv);
6003 if (!StringBeginsWith(url, u"about:neterror"_ns) &&
6004 !mBrowsingContext->HadOriginalOpener() && !aTrustedCaller &&
6005 !IsOnlyTopLevelDocumentInSHistory()) {
6006 bool allowClose =
6007 mAllowScriptsToClose ||
6008 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
6009 if (!allowClose) {
6010 // We're blocking the close operation
6011 // report localized error msg in JS console
6012 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
6013 "DOM Window"_ns,
6014 mDoc, // Better name for the category?
6015 nsContentUtils::eDOM_PROPERTIES,
6016 "WindowCloseByScriptBlockedWarning");
6018 return;
6023 if (!mInClose && !mIsClosed && !CanClose()) {
6024 return;
6027 // Fire a DOM event notifying listeners that this window is about to
6028 // be closed. The tab UI code may choose to cancel the default
6029 // action for this event, if so, we won't actually close the window
6030 // (since the tab UI code will close the tab in stead). Sure, this
6031 // could be abused by content code, but do we care? I don't think
6032 // so...
6034 bool wasInClose = mInClose;
6035 mInClose = true;
6037 if (!DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes)) {
6038 // Someone chose to prevent the default action for this event, if
6039 // so, let's not close this window after all...
6041 mInClose = wasInClose;
6042 return;
6045 FinalClose();
6048 bool nsGlobalWindowOuter::IsOnlyTopLevelDocumentInSHistory() {
6049 NS_ENSURE_TRUE(mDocShell && mBrowsingContext, false);
6050 // Disabled since IsFrame() is buggy in Fission
6051 // MOZ_ASSERT(mBrowsingContext->IsTop());
6053 if (mozilla::SessionHistoryInParent()) {
6054 return mBrowsingContext->GetIsSingleToplevelInHistory();
6057 RefPtr<ChildSHistory> csh = nsDocShell::Cast(mDocShell)->GetSessionHistory();
6058 if (csh && csh->LegacySHistory()) {
6059 return csh->LegacySHistory()->IsEmptyOrHasEntriesForSingleTopLevelPage();
6062 return false;
6065 nsresult nsGlobalWindowOuter::Close() {
6066 CloseOuter(/* aTrustedCaller = */ true);
6067 return NS_OK;
6070 void nsGlobalWindowOuter::ForceClose() {
6071 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
6073 if (mBrowsingContext->IsSubframe() || !mDocShell) {
6074 // This may be a frame in a frameset, or a window that's already closed.
6075 // Ignore such calls.
6076 return;
6079 if (mHavePendingClose) {
6080 // We're going to be closed anyway; do nothing since we don't want
6081 // to double-close
6082 return;
6085 mInClose = true;
6087 DispatchCustomEvent(u"DOMWindowClose"_ns, ChromeOnlyDispatch::eYes);
6089 FinalClose();
6092 void nsGlobalWindowOuter::FinalClose() {
6093 // Flag that we were closed.
6094 mIsClosed = true;
6096 if (!mBrowsingContext->IsDiscarded()) {
6097 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetClosed(true));
6100 // If we get here from CloseOuter then it means that the parent process is
6101 // going to close our window for us. It's just important to set mIsClosed.
6102 if (XRE_GetProcessType() == GeckoProcessType_Content) {
6103 return;
6106 // This stuff is non-sensical but incredibly fragile. The reasons for the
6107 // behavior here don't make sense today and may not have ever made sense,
6108 // but various bits of frontend code break when you change them. If you need
6109 // to fix up this behavior, feel free to. It's a righteous task, but involves
6110 // wrestling with various download manager tests, frontend code, and possible
6111 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
6112 // testing ground.
6114 // In particular, if some inner of |win| is the entry global, we must
6115 // complete _two_ round-trips to the event loop before the call to
6116 // ReallyCloseWindow. This allows setTimeout handlers that are set after
6117 // FinalClose() is called to run before the window is torn down.
6118 nsCOMPtr<nsPIDOMWindowInner> entryWindow =
6119 do_QueryInterface(GetEntryGlobal());
6120 bool indirect = entryWindow && entryWindow->GetOuterWindow() == this;
6121 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
6122 ReallyCloseWindow();
6123 } else {
6124 mHavePendingClose = true;
6128 void nsGlobalWindowOuter::ReallyCloseWindow() {
6129 // Make sure we never reenter this method.
6130 mHavePendingClose = true;
6132 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6133 if (!treeOwnerAsWin) {
6134 return;
6137 treeOwnerAsWin->Destroy();
6138 CleanUp();
6141 void nsGlobalWindowOuter::SuppressEventHandling() {
6142 if (mSuppressEventHandlingDepth == 0) {
6143 if (BrowsingContext* bc = GetBrowsingContext()) {
6144 bc->PreOrderWalk([&](BrowsingContext* aBC) {
6145 if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) {
6146 if (RefPtr<Document> doc = win->GetExtantDoc()) {
6147 mSuspendedDocs.AppendElement(doc);
6148 // Note: Document::SuppressEventHandling will also automatically
6149 // suppress event handling for any in-process sub-documents.
6150 // However, since we need to deal with cases where remote
6151 // BrowsingContexts may be interleaved with in-process ones, we
6152 // still need to walk the entire tree ourselves. This may be
6153 // slightly redundant in some cases, but since event handling
6154 // suppressions maintain a count of current blockers, it does not
6155 // cause any problems.
6156 doc->SuppressEventHandling();
6162 mSuppressEventHandlingDepth++;
6165 void nsGlobalWindowOuter::UnsuppressEventHandling() {
6166 MOZ_ASSERT(mSuppressEventHandlingDepth != 0);
6167 mSuppressEventHandlingDepth--;
6169 if (mSuppressEventHandlingDepth == 0 && mSuspendedDocs.Length()) {
6170 RefPtr<Document> currentDoc = GetExtantDoc();
6171 bool fireEvent = currentDoc == mSuspendedDocs[0];
6172 nsTArray<RefPtr<Document>> suspendedDocs = std::move(mSuspendedDocs);
6173 for (const auto& doc : suspendedDocs) {
6174 doc->UnsuppressEventHandlingAndFireEvents(fireEvent);
6179 nsGlobalWindowOuter* nsGlobalWindowOuter::EnterModalState() {
6180 // GetInProcessScriptableTop, not GetInProcessTop, so that EnterModalState
6181 // works properly with <iframe mozbrowser>.
6182 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
6184 if (!topWin) {
6185 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
6186 return nullptr;
6189 // If there is an active ESM in this window, clear it. Otherwise, this can
6190 // cause a problem if a modal state is entered during a mouseup event.
6191 EventStateManager* activeESM = static_cast<EventStateManager*>(
6192 EventStateManager::GetActiveEventStateManager());
6193 if (activeESM && activeESM->GetPresContext()) {
6194 PresShell* activePresShell = activeESM->GetPresContext()->GetPresShell();
6195 if (activePresShell && (nsContentUtils::ContentIsCrossDocDescendantOf(
6196 activePresShell->GetDocument(), mDoc) ||
6197 nsContentUtils::ContentIsCrossDocDescendantOf(
6198 mDoc, activePresShell->GetDocument()))) {
6199 EventStateManager::ClearGlobalActiveContent(activeESM);
6201 PresShell::ReleaseCapturingContent();
6203 if (activePresShell) {
6204 RefPtr<nsFrameSelection> frameSelection =
6205 activePresShell->FrameSelection();
6206 frameSelection->SetDragState(false);
6211 // If there are any drag and drop operations in flight, try to end them.
6212 nsCOMPtr<nsIDragService> ds =
6213 do_GetService("@mozilla.org/widget/dragservice;1");
6214 if (ds) {
6215 ds->EndDragSession(true, 0);
6218 // Clear the capturing content if it is under topDoc.
6219 // Usually the activeESM check above does that, but there are cases when
6220 // we don't have activeESM, or it is for different document.
6221 Document* topDoc = topWin->GetExtantDoc();
6222 nsIContent* capturingContent = PresShell::GetCapturingContent();
6223 if (capturingContent && topDoc &&
6224 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
6225 PresShell::ReleaseCapturingContent();
6228 if (topWin->mModalStateDepth == 0) {
6229 topWin->SuppressEventHandling();
6231 if (nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(topWin)) {
6232 inner->Suspend();
6235 topWin->mModalStateDepth++;
6236 return topWin;
6239 void nsGlobalWindowOuter::LeaveModalState() {
6241 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
6242 if (!topWin) {
6243 NS_WARNING("Uh, LeaveModalState() called w/o a reachable top window?");
6244 return;
6247 if (topWin != this) {
6248 MOZ_ASSERT(IsSuspended());
6249 return topWin->LeaveModalState();
6253 MOZ_ASSERT(mModalStateDepth != 0);
6254 MOZ_ASSERT(IsSuspended());
6255 mModalStateDepth--;
6257 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
6258 if (mModalStateDepth == 0) {
6259 if (inner) {
6260 inner->Resume();
6263 UnsuppressEventHandling();
6266 // Remember the time of the last dialog quit.
6267 if (auto* bcg = GetBrowsingContextGroup()) {
6268 bcg->SetLastDialogQuitTime(TimeStamp::Now());
6271 if (mModalStateDepth == 0) {
6272 RefPtr<Event> event = NS_NewDOMEvent(inner, nullptr, nullptr);
6273 event->InitEvent(u"endmodalstate"_ns, true, false);
6274 event->SetTrusted(true);
6275 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
6276 DispatchEvent(*event);
6280 bool nsGlobalWindowOuter::IsInModalState() {
6281 nsGlobalWindowOuter* topWin = GetInProcessScriptableTopInternal();
6283 if (!topWin) {
6284 // IsInModalState() getting called w/o a reachable top window is a bit
6285 // iffy, but valid enough not to make noise about it. See bug 404828
6286 return false;
6289 return topWin->mModalStateDepth != 0;
6292 void nsGlobalWindowOuter::NotifyWindowIDDestroyed(const char* aTopic) {
6293 nsCOMPtr<nsIRunnable> runnable =
6294 new WindowDestroyedEvent(this, mWindowID, aTopic);
6295 Dispatch(runnable.forget());
6298 Element* nsGlobalWindowOuter::GetFrameElement(nsIPrincipal& aSubjectPrincipal) {
6299 // Per HTML5, the frameElement getter returns null in cross-origin situations.
6300 Element* element = GetFrameElement();
6301 if (!element) {
6302 return nullptr;
6305 if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) {
6306 return nullptr;
6309 return element;
6312 Element* nsGlobalWindowOuter::GetFrameElement() {
6313 if (!mBrowsingContext || mBrowsingContext->IsTop()) {
6314 return nullptr;
6316 return mBrowsingContext->GetEmbedderElement();
6319 namespace {
6320 class ChildCommandDispatcher : public Runnable {
6321 public:
6322 ChildCommandDispatcher(nsPIWindowRoot* aRoot, nsIBrowserChild* aBrowserChild,
6323 nsPIDOMWindowOuter* aWindow, const nsAString& aAction)
6324 : mozilla::Runnable("ChildCommandDispatcher"),
6325 mRoot(aRoot),
6326 mBrowserChild(aBrowserChild),
6327 mWindow(aWindow),
6328 mAction(aAction) {}
6330 NS_IMETHOD Run() override {
6331 AutoTArray<nsCString, 70> enabledCommands, disabledCommands;
6332 mRoot->GetEnabledDisabledCommands(enabledCommands, disabledCommands);
6333 if (enabledCommands.Length() || disabledCommands.Length()) {
6334 BrowserChild* bc = static_cast<BrowserChild*>(mBrowserChild.get());
6335 bc->SendEnableDisableCommands(mWindow->GetBrowsingContext(), mAction,
6336 enabledCommands, disabledCommands);
6339 return NS_OK;
6342 private:
6343 nsCOMPtr<nsPIWindowRoot> mRoot;
6344 nsCOMPtr<nsIBrowserChild> mBrowserChild;
6345 nsCOMPtr<nsPIDOMWindowOuter> mWindow;
6346 nsString mAction;
6349 class CommandDispatcher : public Runnable {
6350 public:
6351 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
6352 const nsAString& aAction)
6353 : mozilla::Runnable("CommandDispatcher"),
6354 mDispatcher(aDispatcher),
6355 mAction(aAction) {}
6357 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
6358 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
6359 return mDispatcher->UpdateCommands(mAction);
6362 const nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
6363 nsString mAction;
6365 } // anonymous namespace
6367 void nsGlobalWindowOuter::UpdateCommands(const nsAString& anAction) {
6368 // If this is a child process, redirect to the parent process.
6369 if (nsIDocShell* docShell = GetDocShell()) {
6370 if (nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild()) {
6371 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
6372 if (root) {
6373 nsContentUtils::AddScriptRunner(
6374 new ChildCommandDispatcher(root, child, this, anAction));
6376 return;
6380 nsPIDOMWindowOuter* rootWindow = GetPrivateRoot();
6381 if (!rootWindow) {
6382 return;
6385 Document* doc = rootWindow->GetExtantDoc();
6387 if (!doc) {
6388 return;
6391 // Retrieve the command dispatcher and call updateCommands on it.
6392 nsIDOMXULCommandDispatcher* xulCommandDispatcher =
6393 doc->GetCommandDispatcher();
6394 if (xulCommandDispatcher) {
6395 nsContentUtils::AddScriptRunner(
6396 new CommandDispatcher(xulCommandDispatcher, anAction));
6400 Selection* nsGlobalWindowOuter::GetSelectionOuter() {
6401 if (!mDocShell) {
6402 return nullptr;
6405 PresShell* presShell = mDocShell->GetPresShell();
6406 if (!presShell) {
6407 return nullptr;
6409 return presShell->GetCurrentSelection(SelectionType::eNormal);
6412 already_AddRefed<Selection> nsGlobalWindowOuter::GetSelection() {
6413 RefPtr<Selection> selection = GetSelectionOuter();
6414 return selection.forget();
6417 bool nsGlobalWindowOuter::FindOuter(const nsAString& aString,
6418 bool aCaseSensitive, bool aBackwards,
6419 bool aWrapAround, bool aWholeWord,
6420 bool aSearchInFrames, bool aShowDialog,
6421 ErrorResult& aError) {
6422 Unused << aShowDialog;
6424 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
6425 if (!finder) {
6426 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6427 return false;
6430 // Set the options of the search
6431 aError = finder->SetSearchString(aString);
6432 if (aError.Failed()) {
6433 return false;
6435 finder->SetMatchCase(aCaseSensitive);
6436 finder->SetFindBackwards(aBackwards);
6437 finder->SetWrapFind(aWrapAround);
6438 finder->SetEntireWord(aWholeWord);
6439 finder->SetSearchFrames(aSearchInFrames);
6441 // the nsIWebBrowserFind is initialized to use this window
6442 // as the search root, but uses focus to set the current search
6443 // frame. If we're being called from JS (as here), this window
6444 // should be the current search frame.
6445 nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
6446 if (framesFinder) {
6447 framesFinder->SetRootSearchFrame(this); // paranoia
6448 framesFinder->SetCurrentSearchFrame(this);
6451 if (aString.IsEmpty()) {
6452 return false;
6455 // Launch the search with the passed in search string
6456 bool didFind = false;
6457 aError = finder->FindNext(&didFind);
6458 return didFind;
6461 //*****************************************************************************
6462 // EventTarget
6463 //*****************************************************************************
6465 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() {
6466 return this;
6469 nsIGlobalObject* nsGlobalWindowOuter::GetOwnerGlobal() const {
6470 return GetCurrentInnerWindowInternal(this);
6473 bool nsGlobalWindowOuter::DispatchEvent(Event& aEvent, CallerType aCallerType,
6474 ErrorResult& aRv) {
6475 FORWARD_TO_INNER(DispatchEvent, (aEvent, aCallerType, aRv), false);
6478 bool nsGlobalWindowOuter::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
6479 // It's OK that we just return false here on failure to create an
6480 // inner. GetOrCreateListenerManager() will likewise fail, and then
6481 // we won't be adding any listeners anyway.
6482 FORWARD_TO_INNER_CREATE(ComputeDefaultWantsUntrusted, (aRv), false);
6485 EventListenerManager* nsGlobalWindowOuter::GetOrCreateListenerManager() {
6486 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
6489 EventListenerManager* nsGlobalWindowOuter::GetExistingListenerManager() const {
6490 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
6493 //*****************************************************************************
6494 // nsGlobalWindowOuter::nsPIDOMWindow
6495 //*****************************************************************************
6497 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateParent() {
6498 nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent();
6500 if (this == parent) {
6501 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
6502 if (!chromeElement)
6503 return nullptr; // This is ok, just means a null parent.
6505 Document* doc = chromeElement->GetComposedDoc();
6506 if (!doc) return nullptr; // This is ok, just means a null parent.
6508 return doc->GetWindow();
6511 return parent;
6514 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateRoot() {
6515 nsCOMPtr<nsPIDOMWindowOuter> top = GetInProcessTop();
6517 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
6518 if (chromeElement) {
6519 Document* doc = chromeElement->GetComposedDoc();
6520 if (doc) {
6521 nsCOMPtr<nsPIDOMWindowOuter> parent = doc->GetWindow();
6522 if (parent) {
6523 top = parent->GetInProcessTop();
6528 return top;
6531 // This has a caller in Windows-only code (nsNativeAppSupportWin).
6532 Location* nsGlobalWindowOuter::GetLocation() {
6533 // This method can be called on the outer window as well.
6534 FORWARD_TO_INNER(Location, (), nullptr);
6537 void nsGlobalWindowOuter::SetIsBackground(bool aIsBackground) {
6538 bool changed = aIsBackground != IsBackground();
6539 SetIsBackgroundInternal(aIsBackground);
6541 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
6543 if (inner && changed) {
6544 inner->UpdateBackgroundState();
6547 if (aIsBackground) {
6548 // Notify gamepadManager we are at the background window,
6549 // we need to stop vibrate.
6550 // Stop the vr telemery time spent when it switches to
6551 // the background window.
6552 if (inner && changed) {
6553 inner->StopGamepadHaptics();
6554 inner->StopVRActivity();
6555 // true is for asking to set the delta time to
6556 // the telemetry.
6557 inner->ResetVRTelemetry(true);
6559 return;
6562 if (inner) {
6563 // When switching to be as a top tab, restart the telemetry.
6564 // false is for only resetting the timestamp.
6565 inner->ResetVRTelemetry(false);
6566 inner->SyncGamepadState();
6567 inner->StartVRActivity();
6571 void nsGlobalWindowOuter::SetIsBackgroundInternal(bool aIsBackground) {
6572 mIsBackground = aIsBackground;
6575 void nsGlobalWindowOuter::SetChromeEventHandler(
6576 EventTarget* aChromeEventHandler) {
6577 SetChromeEventHandlerInternal(aChromeEventHandler);
6578 // update the chrome event handler on all our inner windows
6579 RefPtr<nsGlobalWindowInner> inner;
6580 for (PRCList* node = PR_LIST_HEAD(this); node != this;
6581 node = PR_NEXT_LINK(inner)) {
6582 // This cast is only safe if `node != this`, as nsGlobalWindowOuter is also
6583 // in the list.
6584 inner = static_cast<nsGlobalWindowInner*>(node);
6585 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
6586 "bad outer window pointer");
6587 inner->SetChromeEventHandlerInternal(aChromeEventHandler);
6591 void nsGlobalWindowOuter::SetFocusedElement(Element* aElement,
6592 uint32_t aFocusMethod,
6593 bool aNeedsFocus) {
6594 FORWARD_TO_INNER_VOID(SetFocusedElement,
6595 (aElement, aFocusMethod, aNeedsFocus));
6598 uint32_t nsGlobalWindowOuter::GetFocusMethod() {
6599 FORWARD_TO_INNER(GetFocusMethod, (), 0);
6602 bool nsGlobalWindowOuter::ShouldShowFocusRing() {
6603 FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
6606 bool nsGlobalWindowOuter::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
6607 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
6610 void nsGlobalWindowOuter::SetReadyForFocus() {
6611 FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
6614 void nsGlobalWindowOuter::PageHidden() {
6615 FORWARD_TO_INNER_VOID(PageHidden, ());
6618 already_AddRefed<nsICSSDeclaration>
6619 nsGlobalWindowOuter::GetComputedStyleHelperOuter(Element& aElt,
6620 const nsAString& aPseudoElt,
6621 bool aDefaultStylesOnly,
6622 ErrorResult& aRv) {
6623 if (!mDoc) {
6624 return nullptr;
6627 RefPtr<nsICSSDeclaration> compStyle = NS_NewComputedDOMStyle(
6628 &aElt, aPseudoElt, mDoc,
6629 aDefaultStylesOnly ? nsComputedDOMStyle::StyleType::DefaultOnly
6630 : nsComputedDOMStyle::StyleType::All,
6631 aRv);
6633 return compStyle.forget();
6636 //*****************************************************************************
6637 // nsGlobalWindowOuter::nsIInterfaceRequestor
6638 //*****************************************************************************
6640 nsresult nsGlobalWindowOuter::GetInterfaceInternal(const nsIID& aIID,
6641 void** aSink) {
6642 NS_ENSURE_ARG_POINTER(aSink);
6643 *aSink = nullptr;
6645 if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) {
6646 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6647 webNav.forget(aSink);
6648 } else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
6649 nsCOMPtr<nsIDocShell> docShell = mDocShell;
6650 docShell.forget(aSink);
6652 #ifdef NS_PRINTING
6653 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
6654 if (mDocShell) {
6655 nsCOMPtr<nsIDocumentViewer> viewer;
6656 mDocShell->GetDocViewer(getter_AddRefs(viewer));
6657 if (viewer) {
6658 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer));
6659 webBrowserPrint.forget(aSink);
6663 #endif
6664 else if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
6665 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(mDocShell));
6666 loadContext.forget(aSink);
6669 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
6672 NS_IMETHODIMP
6673 nsGlobalWindowOuter::GetInterface(const nsIID& aIID, void** aSink) {
6674 nsresult rv = GetInterfaceInternal(aIID, aSink);
6675 if (rv == NS_ERROR_NO_INTERFACE) {
6676 return QueryInterface(aIID, aSink);
6678 return rv;
6681 bool nsGlobalWindowOuter::IsSuspended() const {
6682 MOZ_ASSERT(NS_IsMainThread());
6683 // No inner means we are effectively suspended
6684 if (!mInnerWindow) {
6685 return true;
6687 return mInnerWindow->IsSuspended();
6690 bool nsGlobalWindowOuter::IsFrozen() const {
6691 MOZ_ASSERT(NS_IsMainThread());
6692 // No inner means we are effectively frozen
6693 if (!mInnerWindow) {
6694 return true;
6696 return mInnerWindow->IsFrozen();
6699 nsresult nsGlobalWindowOuter::FireDelayedDOMEvents(bool aIncludeSubWindows) {
6700 FORWARD_TO_INNER(FireDelayedDOMEvents, (aIncludeSubWindows),
6701 NS_ERROR_UNEXPECTED);
6704 //*****************************************************************************
6705 // nsGlobalWindowOuter: Window Control Functions
6706 //*****************************************************************************
6708 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetInProcessParentInternal() {
6709 nsCOMPtr<nsPIDOMWindowOuter> parent = GetInProcessParent();
6711 if (parent && parent != this) {
6712 return parent;
6715 return nullptr;
6718 void nsGlobalWindowOuter::UnblockScriptedClosing() {
6719 mBlockScriptedClosingFlag = false;
6722 class AutoUnblockScriptClosing {
6723 private:
6724 RefPtr<nsGlobalWindowOuter> mWin;
6726 public:
6727 explicit AutoUnblockScriptClosing(nsGlobalWindowOuter* aWin) : mWin(aWin) {
6728 MOZ_ASSERT(mWin);
6730 ~AutoUnblockScriptClosing() {
6731 void (nsGlobalWindowOuter::*run)() =
6732 &nsGlobalWindowOuter::UnblockScriptedClosing;
6733 nsCOMPtr<nsIRunnable> caller = NewRunnableMethod(
6734 "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin, run);
6735 mWin->Dispatch(caller.forget());
6739 nsresult nsGlobalWindowOuter::OpenInternal(
6740 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
6741 bool aDialog, bool aContentModal, bool aCalledNoScript, bool aDoJSFixups,
6742 bool aNavigate, nsIArray* argv, nsISupports* aExtraArgument,
6743 nsDocShellLoadState* aLoadState, bool aForceNoOpener, PrintKind aPrintKind,
6744 BrowsingContext** aReturn) {
6745 #ifdef DEBUG
6746 uint32_t argc = 0;
6747 if (argv) argv->GetLength(&argc);
6748 #endif
6750 MOZ_ASSERT(!aExtraArgument || (!argv && argc == 0),
6751 "Can't pass in arguments both ways");
6752 MOZ_ASSERT(!aCalledNoScript || (!argv && argc == 0),
6753 "Can't pass JS args when called via the noscript methods");
6755 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
6757 // Calls to window.open from script should navigate.
6758 MOZ_ASSERT(aCalledNoScript || aNavigate);
6760 *aReturn = nullptr;
6762 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
6763 if (!chrome) {
6764 // No chrome means we don't want to go through with this open call
6765 // -- see nsIWindowWatcher.idl
6766 return NS_ERROR_NOT_AVAILABLE;
6769 NS_ASSERTION(mDocShell, "Must have docshell here");
6771 NS_ConvertUTF16toUTF8 optionsUtf8(aOptions);
6773 WindowFeatures features;
6774 if (!features.Tokenize(optionsUtf8)) {
6775 return NS_ERROR_FAILURE;
6778 bool forceNoOpener = aForceNoOpener;
6779 if (features.Exists("noopener")) {
6780 forceNoOpener = features.GetBool("noopener");
6781 features.Remove("noopener");
6784 bool forceNoReferrer = false;
6785 if (features.Exists("noreferrer")) {
6786 forceNoReferrer = features.GetBool("noreferrer");
6787 if (forceNoReferrer) {
6788 // noreferrer implies noopener
6789 forceNoOpener = true;
6791 features.Remove("noreferrer");
6794 nsAutoCString options;
6795 features.Stringify(options);
6797 // If noopener is force-enabled for the current document, then set noopener to
6798 // true, and clear the name to "_blank".
6799 nsAutoString windowName(aName);
6800 if (nsDocShell::Cast(GetDocShell())->NoopenerForceEnabled()) {
6801 // FIXME: Eventually bypass force-enabling noopener if `aPrintKind !=
6802 // PrintKind::None`, so that we can print pages with noopener force-enabled.
6803 // This will require relaxing assertions elsewhere.
6804 if (aPrintKind != PrintKind::None) {
6805 NS_WARNING(
6806 "printing frames with noopener force-enabled isn't supported yet");
6807 return NS_ERROR_FAILURE;
6810 MOZ_DIAGNOSTIC_ASSERT(aNavigate,
6811 "cannot OpenNoNavigate if noopener is force-enabled");
6813 forceNoOpener = true;
6814 windowName = u"_blank"_ns;
6817 bool windowExists = WindowExists(windowName, forceNoOpener, !aCalledNoScript);
6819 // XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
6820 // (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run.
6821 // But note that if you change this to GetEntryGlobal(), say, then
6822 // OnLinkClickEvent::Run will need a full-blown AutoEntryScript.
6823 const bool checkForPopup =
6824 !nsContentUtils::LegacyIsCallerChromeOrNativeCode() && !aDialog &&
6825 !windowExists;
6827 // Note: the Void handling here is very important, because the window watcher
6828 // expects a null URL string (not an empty string) if there is no URL to load.
6829 nsCString url;
6830 url.SetIsVoid(true);
6831 nsresult rv = NS_OK;
6833 nsCOMPtr<nsIURI> uri;
6835 // It's important to do this security check before determining whether this
6836 // window opening should be blocked, to ensure that we don't FireAbuseEvents
6837 // for a window opening that wouldn't have succeeded in the first place.
6838 if (!aUrl.IsEmpty()) {
6839 AppendUTF16toUTF8(aUrl, url);
6841 // It's safe to skip the security check below if we're not a dialog
6842 // because window.openDialog is not callable from content script. See bug
6843 // 56851.
6845 // If we're not navigating, we assume that whoever *does* navigate the
6846 // window will do a security check of their own.
6847 if (!url.IsVoid() && !aDialog && aNavigate)
6848 rv = SecurityCheckURL(url.get(), getter_AddRefs(uri));
6849 } else if (mDoc) {
6850 mDoc->SetUseCounter(eUseCounter_custom_WindowOpenEmptyUrl);
6853 if (NS_FAILED(rv)) return rv;
6855 UserActivation::Modifiers modifiers;
6856 mBrowsingContext->GetUserActivationModifiersForPopup(&modifiers);
6858 PopupBlocker::PopupControlState abuseLevel =
6859 PopupBlocker::GetPopupControlState();
6860 if (checkForPopup) {
6861 abuseLevel = mBrowsingContext->RevisePopupAbuseLevel(abuseLevel);
6862 if (abuseLevel >= PopupBlocker::openBlocked) {
6863 if (!aCalledNoScript) {
6864 // If script in some other window is doing a window.open on us and
6865 // it's being blocked, then it's OK to close us afterwards, probably.
6866 // But if we're doing a window.open on ourselves and block the popup,
6867 // prevent this window from closing until after this script terminates
6868 // so that whatever popup blocker UI the app has will be visible.
6869 nsCOMPtr<nsPIDOMWindowInner> entryWindow =
6870 do_QueryInterface(GetEntryGlobal());
6871 // Note that entryWindow can be null here if some JS component was the
6872 // place where script was entered for this JS execution.
6873 if (entryWindow && entryWindow->GetOuterWindow() == this) {
6874 mBlockScriptedClosingFlag = true;
6875 closeUnblocker.emplace(this);
6879 FireAbuseEvents(aUrl, windowName, aOptions);
6880 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
6884 RefPtr<BrowsingContext> domReturn;
6886 nsCOMPtr<nsIWindowWatcher> wwatch =
6887 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
6888 NS_ENSURE_TRUE(wwatch, rv);
6890 NS_ConvertUTF16toUTF8 name(windowName);
6892 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
6893 NS_ENSURE_STATE(pwwatch);
6895 MOZ_ASSERT_IF(checkForPopup, abuseLevel < PopupBlocker::openBlocked);
6896 // At this point we should know for a fact that if checkForPopup then
6897 // abuseLevel < PopupBlocker::openBlocked, so we could just check for
6898 // abuseLevel == PopupBlocker::openControlled. But let's be defensive just in
6899 // case and treat anything that fails the above assert as a spam popup too, if
6900 // it ever happens.
6901 bool isPopupSpamWindow =
6902 checkForPopup && (abuseLevel >= PopupBlocker::openControlled);
6904 const auto wwPrintKind = [&] {
6905 switch (aPrintKind) {
6906 case PrintKind::None:
6907 return nsPIWindowWatcher::PRINT_NONE;
6908 case PrintKind::InternalPrint:
6909 return nsPIWindowWatcher::PRINT_INTERNAL;
6910 case PrintKind::WindowDotPrint:
6911 return nsPIWindowWatcher::PRINT_WINDOW_DOT_PRINT;
6913 MOZ_ASSERT_UNREACHABLE("Wat");
6914 return nsPIWindowWatcher::PRINT_NONE;
6915 }();
6918 // Reset popup state while opening a window to prevent the
6919 // current state from being active the whole time a modal
6920 // dialog is open.
6921 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
6923 if (!aCalledNoScript) {
6924 // We asserted at the top of this function that aNavigate is true for
6925 // !aCalledNoScript.
6926 rv = pwwatch->OpenWindow2(this, url, name, options, modifiers,
6927 /* aCalledFromScript = */ true, aDialog,
6928 aNavigate, argv, isPopupSpamWindow,
6929 forceNoOpener, forceNoReferrer, wwPrintKind,
6930 aLoadState, getter_AddRefs(domReturn));
6931 } else {
6932 // Force a system caller here so that the window watcher won't screw us
6933 // up. We do NOT want this case looking at the JS context on the stack
6934 // when searching. Compare comments on
6935 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
6937 // Note: Because nsWindowWatcher is so broken, it's actually important
6938 // that we don't force a system caller here, because that screws it up
6939 // when it tries to compute the caller principal to associate with dialog
6940 // arguments. That whole setup just really needs to be rewritten. :-(
6941 Maybe<AutoNoJSAPI> nojsapi;
6942 if (!aContentModal) {
6943 nojsapi.emplace();
6946 rv = pwwatch->OpenWindow2(this, url, name, options, modifiers,
6947 /* aCalledFromScript = */ false, aDialog,
6948 aNavigate, aExtraArgument, isPopupSpamWindow,
6949 forceNoOpener, forceNoReferrer, wwPrintKind,
6950 aLoadState, getter_AddRefs(domReturn));
6954 NS_ENSURE_SUCCESS(rv, rv);
6956 // success!
6958 if (!aCalledNoScript && !windowExists && uri && !forceNoOpener) {
6959 MaybeAllowStorageForOpenedWindow(uri);
6962 if (domReturn && aDoJSFixups) {
6963 nsPIDOMWindowOuter* outer = domReturn->GetDOMWindow();
6964 if (outer && !nsGlobalWindowOuter::Cast(outer)->IsChromeWindow()) {
6965 // A new non-chrome window was created from a call to
6966 // window.open() from JavaScript, make sure there's a document in
6967 // the new window. We do this by simply asking the new window for
6968 // its document, this will synchronously create an empty document
6969 // if there is no document in the window.
6970 // XXXbz should this just use EnsureInnerWindow()?
6972 // Force document creation.
6973 nsCOMPtr<Document> doc = outer->GetDoc();
6974 Unused << doc;
6978 domReturn.forget(aReturn);
6979 return NS_OK;
6982 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI) {
6983 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
6984 if (NS_WARN_IF(!inner)) {
6985 return;
6988 // No 3rd party URL/window.
6989 if (!AntiTrackingUtils::IsThirdPartyWindow(inner, aURI)) {
6990 return;
6993 Document* doc = inner->GetDoc();
6994 if (!doc) {
6995 return;
6997 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
6998 aURI, doc->NodePrincipal()->OriginAttributesRef());
7000 // We don't care when the asynchronous work finishes here.
7001 // Without e10s or fission enabled this is run in the parent process.
7002 if (XRE_IsParentProcess()) {
7003 Unused << StorageAccessAPIHelper::AllowAccessForOnParentProcess(
7004 principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener);
7005 } else {
7006 Unused << StorageAccessAPIHelper::AllowAccessForOnChildProcess(
7007 principal, GetBrowsingContext(), ContentBlockingNotifier::eOpener);
7011 //*****************************************************************************
7012 // nsGlobalWindowOuter: Helper Functions
7013 //*****************************************************************************
7015 already_AddRefed<nsIDocShellTreeOwner> nsPIDOMWindowOuter::GetTreeOwner() {
7016 // If there's no docShellAsItem, this window must have been closed,
7017 // in that case there is no tree owner.
7019 if (!mDocShell) {
7020 return nullptr;
7023 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
7024 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
7025 return treeOwner.forget();
7028 already_AddRefed<nsIBaseWindow> nsPIDOMWindowOuter::GetTreeOwnerWindow() {
7029 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
7031 // If there's no mDocShell, this window must have been closed,
7032 // in that case there is no tree owner.
7034 if (mDocShell) {
7035 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
7038 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
7039 return baseWindow.forget();
7042 already_AddRefed<nsIWebBrowserChrome>
7043 nsPIDOMWindowOuter::GetWebBrowserChrome() {
7044 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
7046 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
7047 return browserChrome.forget();
7050 nsIScrollableFrame* nsGlobalWindowOuter::GetScrollFrame() {
7051 if (!mDocShell) {
7052 return nullptr;
7055 PresShell* presShell = mDocShell->GetPresShell();
7056 if (presShell) {
7057 return presShell->GetRootScrollFrameAsScrollable();
7059 return nullptr;
7062 nsresult nsGlobalWindowOuter::SecurityCheckURL(const char* aURL,
7063 nsIURI** aURI) {
7064 nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
7065 do_QueryInterface(GetEntryGlobal());
7066 if (!sourceWindow) {
7067 sourceWindow = GetCurrentInnerWindow();
7069 AutoJSContext cx;
7070 nsGlobalWindowInner* sourceWin = nsGlobalWindowInner::Cast(sourceWindow);
7071 JSAutoRealm ar(cx, sourceWin->GetGlobalJSObject());
7073 // Resolve the baseURI, which could be relative to the calling window.
7075 // Note the algorithm to get the base URI should match the one
7076 // used to actually kick off the load in nsWindowWatcher.cpp.
7077 nsCOMPtr<Document> doc = sourceWindow->GetDoc();
7078 nsIURI* baseURI = nullptr;
7079 auto encoding = UTF_8_ENCODING; // default to utf-8
7080 if (doc) {
7081 baseURI = doc->GetDocBaseURI();
7082 encoding = doc->GetDocumentCharacterSet();
7084 nsCOMPtr<nsIURI> uri;
7085 nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
7086 encoding, baseURI);
7087 if (NS_WARN_IF(NS_FAILED(rv))) {
7088 return NS_ERROR_DOM_SYNTAX_ERR;
7091 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript(
7092 cx, uri))) {
7093 return NS_ERROR_FAILURE;
7096 uri.forget(aURI);
7097 return NS_OK;
7100 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType) {
7101 if (mDoc) {
7102 mDoc->FlushPendingNotifications(aType);
7106 void nsGlobalWindowOuter::EnsureSizeAndPositionUpToDate() {
7107 // If we're a subframe, make sure our size is up to date. Make sure to go
7108 // through the document chain rather than the window chain to not flush on
7109 // detached iframes, see bug 1545516.
7110 if (mDoc && mDoc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
7111 RefPtr<Document> parent = mDoc->GetInProcessParentDocument();
7112 parent->FlushPendingNotifications(FlushType::Layout);
7116 already_AddRefed<nsISupports> nsGlobalWindowOuter::SaveWindowState() {
7117 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7119 if (!mContext || !GetWrapperPreserveColor()) {
7120 // The window may be getting torn down; don't bother saving state.
7121 return nullptr;
7124 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
7125 NS_ASSERTION(inner, "No inner window to save");
7127 if (WindowContext* wc = inner->GetWindowContext()) {
7128 MOZ_ASSERT(!wc->GetWindowStateSaved());
7129 Unused << wc->SetWindowStateSaved(true);
7132 // Don't do anything else to this inner window! After this point, all
7133 // calls to SetTimeoutOrInterval will create entries in the timeout
7134 // list that will only run after this window has come out of the bfcache.
7135 // Also, while we're frozen, we won't dispatch online/offline events
7136 // to the page.
7137 inner->Freeze();
7139 nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
7141 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7142 ("saving window state, state = %p", (void*)state));
7144 return state.forget();
7147 nsresult nsGlobalWindowOuter::RestoreWindowState(nsISupports* aState) {
7148 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7150 if (!mContext || !GetWrapperPreserveColor()) {
7151 // The window may be getting torn down; don't bother restoring state.
7152 return NS_OK;
7155 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
7156 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
7158 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7159 ("restoring window state, state = %p", (void*)holder));
7161 // And we're ready to go!
7162 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal(this);
7164 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
7165 // it easy to tell which link was last clicked when going back a page.
7166 RefPtr<Element> focusedElement = inner->GetFocusedElement();
7167 if (nsContentUtils::ContentIsLink(focusedElement)) {
7168 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
7169 fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL |
7170 nsIFocusManager::FLAG_SHOWRING);
7174 if (WindowContext* wc = inner->GetWindowContext()) {
7175 MOZ_ASSERT(wc->GetWindowStateSaved());
7176 Unused << wc->SetWindowStateSaved(false);
7179 inner->Thaw();
7181 holder->DidRestoreWindow();
7183 return NS_OK;
7186 void nsGlobalWindowOuter::AddSizeOfIncludingThis(
7187 nsWindowSizes& aWindowSizes) const {
7188 aWindowSizes.mDOMSizes.mDOMOtherSize +=
7189 aWindowSizes.mState.mMallocSizeOf(this);
7192 uint32_t nsGlobalWindowOuter::GetAutoActivateVRDisplayID() {
7193 uint32_t retVal = mAutoActivateVRDisplayID;
7194 mAutoActivateVRDisplayID = 0;
7195 return retVal;
7198 void nsGlobalWindowOuter::SetAutoActivateVRDisplayID(
7199 uint32_t aAutoActivateVRDisplayID) {
7200 mAutoActivateVRDisplayID = aAutoActivateVRDisplayID;
7203 already_AddRefed<nsWindowRoot> nsGlobalWindowOuter::GetWindowRootOuter() {
7204 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
7205 return root.forget().downcast<nsWindowRoot>();
7208 nsIDOMWindowUtils* nsGlobalWindowOuter::WindowUtils() {
7209 if (!mWindowUtils) {
7210 mWindowUtils = new nsDOMWindowUtils(this);
7212 return mWindowUtils;
7215 bool nsGlobalWindowOuter::IsInSyncOperation() {
7216 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
7219 // Note: This call will lock the cursor, it will not change as it moves.
7220 // To unlock, the cursor must be set back to Auto.
7221 void nsGlobalWindowOuter::SetCursorOuter(const nsACString& aCursor,
7222 ErrorResult& aError) {
7223 auto cursor = StyleCursorKind::Auto;
7224 if (!Servo_CursorKind_Parse(&aCursor, &cursor)) {
7225 // FIXME: It's a bit weird that this doesn't throw but stuff below does, but
7226 // matches previous behavior so...
7227 return;
7230 RefPtr<nsPresContext> presContext;
7231 if (mDocShell) {
7232 presContext = mDocShell->GetPresContext();
7235 if (presContext) {
7236 // Need root widget.
7237 PresShell* presShell = mDocShell->GetPresShell();
7238 if (!presShell) {
7239 aError.Throw(NS_ERROR_FAILURE);
7240 return;
7243 nsViewManager* vm = presShell->GetViewManager();
7244 if (!vm) {
7245 aError.Throw(NS_ERROR_FAILURE);
7246 return;
7249 nsView* rootView = vm->GetRootView();
7250 if (!rootView) {
7251 aError.Throw(NS_ERROR_FAILURE);
7252 return;
7255 nsIWidget* widget = rootView->GetNearestWidget(nullptr);
7256 if (!widget) {
7257 aError.Throw(NS_ERROR_FAILURE);
7258 return;
7261 // Call esm and set cursor.
7262 aError = presContext->EventStateManager()->SetCursor(
7263 cursor, nullptr, {}, Nothing(), widget, true);
7267 nsIBrowserDOMWindow* nsGlobalWindowOuter::GetBrowserDOMWindow() {
7268 MOZ_RELEASE_ASSERT(IsChromeWindow());
7269 return mChromeFields.mBrowserDOMWindow;
7272 void nsGlobalWindowOuter::SetBrowserDOMWindowOuter(
7273 nsIBrowserDOMWindow* aBrowserWindow) {
7274 MOZ_ASSERT(IsChromeWindow());
7275 mChromeFields.mBrowserDOMWindow = aBrowserWindow;
7278 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetMessageManager() {
7279 if (!mInnerWindow) {
7280 NS_WARNING("No inner window available!");
7281 return nullptr;
7283 return GetCurrentInnerWindowInternal(this)->MessageManager();
7286 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetGroupMessageManager(
7287 const nsAString& aGroup) {
7288 if (!mInnerWindow) {
7289 NS_WARNING("No inner window available!");
7290 return nullptr;
7292 return GetCurrentInnerWindowInternal(this)->GetGroupMessageManager(aGroup);
7295 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7297 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7298 # pragma message( \
7299 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7300 # error "Never include unwrapped windows.h in this file!"
7301 #endif
7303 // Helper called by methods that move/resize the window,
7304 // to ensure the presContext (if any) is aware of resolution
7305 // change that may happen in multi-monitor configuration.
7306 void nsGlobalWindowOuter::CheckForDPIChange() {
7307 if (mDocShell) {
7308 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
7309 if (presContext) {
7310 if (presContext->DeviceContext()->CheckDPIChange()) {
7311 presContext->UIResolutionChanged();
7317 nsresult nsGlobalWindowOuter::Dispatch(
7318 already_AddRefed<nsIRunnable>&& aRunnable) const {
7319 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7320 return NS_DispatchToCurrentThread(std::move(aRunnable));
7323 nsISerialEventTarget* nsGlobalWindowOuter::SerialEventTarget() const {
7324 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7325 return GetMainThreadSerialEventTarget();
7328 void nsGlobalWindowOuter::MaybeResetWindowName(Document* aNewDocument) {
7329 MOZ_ASSERT(aNewDocument);
7331 if (!StaticPrefs::privacy_window_name_update_enabled()) {
7332 return;
7335 const LoadingSessionHistoryInfo* info =
7336 nsDocShell::Cast(mDocShell)->GetLoadingSessionHistoryInfo();
7337 if (!info || info->mForceMaybeResetName.isNothing()) {
7338 // We only reset the window name for the top-level content as well as
7339 // storing in session entries.
7340 if (!GetBrowsingContext()->IsTopContent()) {
7341 return;
7344 // Following implements https://html.spec.whatwg.org/#history-traversal:
7345 // Step 4.2. Check if the loading document has a different origin than the
7346 // previous document.
7348 // We don't need to do anything if we haven't loaded a non-initial document.
7349 if (!GetBrowsingContext()->GetHasLoadedNonInitialDocument()) {
7350 return;
7353 // If we have an existing document, directly check the document prinicpals
7354 // with the new document to know if it is cross-origin.
7356 // Note that there will be an issue of initial document handling in Fission
7357 // when running the WPT unset_context_name-1.html. In the test, the first
7358 // about:blank page would be loaded with the principal of the testing domain
7359 // in Fission and the window.name will be set there. Then, The window.name
7360 // won't be reset after navigating to the testing page because the principal
7361 // is the same. But, it won't be the case for non-Fission mode that the
7362 // first about:blank will be loaded with a null principal and the
7363 // window.name will be reset when loading the test page.
7364 if (mDoc && mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal())) {
7365 return;
7368 // If we don't have an existing document, and if it's not the initial
7369 // about:blank, we could be loading a document because of the
7370 // process-switching. In this case, this should be a cross-origin
7371 // navigation.
7372 } else if (!info->mForceMaybeResetName.ref()) {
7373 return;
7376 // Step 4.2.2 Store the window.name into all session history entries that have
7377 // the same origin as the previous document.
7378 nsDocShell::Cast(mDocShell)->StoreWindowNameToSHEntries();
7380 // Step 4.2.3 Clear the window.name if the browsing context is the top-level
7381 // content and doesn't have an opener.
7383 // We need to reset the window name in case of a cross-origin navigation,
7384 // without an opener.
7385 RefPtr<BrowsingContext> opener = GetOpenerBrowsingContext();
7386 if (opener) {
7387 return;
7390 Unused << mBrowsingContext->SetName(EmptyString());
7393 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
7394 BrowsingContext* aBC) {
7395 BrowsingContextGroup* group = aBC->Group();
7396 if (!group) {
7397 NS_ERROR(
7398 "nsGlobalWindowOuter::TemporarilyDisableDialogs called without a "
7399 "browsing context group?");
7400 return;
7403 if (group) {
7404 mGroup = group;
7405 mSavedDialogsEnabled = group->GetAreDialogsEnabled();
7406 group->SetAreDialogsEnabled(false);
7410 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() {
7411 if (mGroup) {
7412 mGroup->SetAreDialogsEnabled(mSavedDialogsEnabled);
7416 /* static */
7417 already_AddRefed<nsGlobalWindowOuter> nsGlobalWindowOuter::Create(
7418 nsDocShell* aDocShell, bool aIsChrome) {
7419 uint64_t outerWindowID = aDocShell->GetOuterWindowID();
7420 RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
7421 if (aIsChrome) {
7422 window->mIsChrome = true;
7424 window->SetDocShell(aDocShell);
7426 window->InitWasOffline();
7427 return window.forget();
7430 nsIURI* nsPIDOMWindowOuter::GetDocumentURI() const {
7431 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7434 void nsPIDOMWindowOuter::MaybeCreateDoc() {
7435 MOZ_ASSERT(!mDoc);
7436 if (nsIDocShell* docShell = GetDocShell()) {
7437 // Note that |document| here is the same thing as our mDoc, but we
7438 // don't have to explicitly set the member variable because the docshell
7439 // has already called SetNewDocument().
7440 nsCOMPtr<Document> document = docShell->GetDocument();
7441 Unused << document;
7445 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal(
7446 EventTarget* aChromeEventHandler) {
7447 // Out-of-line so we don't need to include ContentFrameMessageManager.h in
7448 // nsPIDOMWindow.h.
7449 mChromeEventHandler = aChromeEventHandler;
7451 // mParentTarget and mMessageManager will be set when the next event is
7452 // dispatched or someone asks for our message manager.
7453 mParentTarget = nullptr;
7454 mMessageManager = nullptr;
7457 mozilla::dom::DocGroup* nsPIDOMWindowOuter::GetDocGroup() const {
7458 Document* doc = GetExtantDoc();
7459 if (doc) {
7460 return doc->GetDocGroup();
7462 return nullptr;
7465 nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID)
7466 : mFrameElement(nullptr),
7467 mModalStateDepth(0),
7468 mSuppressEventHandlingDepth(0),
7469 mIsBackground(false),
7470 mIsRootOuterWindow(false),
7471 mInnerWindow(nullptr),
7472 mWindowID(aWindowID),
7473 mMarkedCCGeneration(0) {}
7475 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() = default;