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"
14 #include "mozilla/MemoryReporting.h"
17 #include "Navigator.h"
18 #include "nsContentSecurityManager.h"
19 #include "nsGlobalWindowOuter.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"
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"
79 #include "nsJSUtils.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"
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"
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"
137 #include "nsDOMString.h"
138 #include "nsThreadUtils.h"
139 #include "nsILoadContext.h"
140 #include "nsIScrollableFrame.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"
178 # include "nsIPrintSettings.h"
179 # include "nsIPrintSettingsService.h"
180 # include "nsIWebBrowserPrint.h"
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"
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"
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"
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"
254 # include "mozilla/dom/SpeechSynthesis.h"
258 # include <android/log.h>
262 # include <process.h>
263 # define getpid _getpid
265 # include <unistd.h> // for getpid()
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) \
283 if (!mInnerWindow) { \
284 NS_WARNING("No inner window available!"); \
287 return GetCurrentInnerWindowInternal(this)->method args; \
290 #define FORWARD_TO_INNER_VOID(method, args) \
292 if (!mInnerWindow) { \
293 NS_WARNING("No inner window available!"); \
296 GetCurrentInnerWindowInternal(this)->method args; \
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) \
304 if (!mInnerWindow) { \
308 nsCOMPtr<Document> kungFuDeathGrip = GetDoc(); \
309 ::mozilla::Unused << kungFuDeathGrip; \
310 if (!mInnerWindow) { \
314 return GetCurrentInnerWindowInternal(this)->method args; \
317 static LazyLogModule
gDOMLeakPRLogOuter("DOMLeakOuter");
318 extern LazyLogModule gPageCacheLog
;
321 static LazyLogModule
gDocShellAndDOMWindowLeakLogging(
322 "DocShellAndDOMWindowLeak");
325 nsGlobalWindowOuter::OuterWindowByIdTable
*
326 nsGlobalWindowOuter::sOuterWindowsById
= nullptr;
329 nsPIDOMWindowOuter
* nsPIDOMWindowOuter::GetFromCurrentInner(
330 nsPIDOMWindowInner
* aInner
) {
335 nsPIDOMWindowOuter
* outer
= aInner
->GetOuterWindow();
336 if (!outer
|| outer
->GetCurrentInnerWindow() != aInner
) {
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
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
>;
364 constexpr nsOuterWindowProxy() : Base(0) {}
366 bool finalizeInBackground(const JS::Value
& priv
) const override
{
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
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
,
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
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
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
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
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
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
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()));
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
,
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
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
)) {
565 void nsOuterWindowProxy::finalize(JS::GCContext
* gcx
, JSObject
* proxy
) const {
566 nsGlobalWindowOuter
* outerWindow
= GetOuterWindow(proxy
);
568 outerWindow
->ClearWrapper(proxy
);
569 BrowsingContext
* bc
= outerWindow
->GetBrowsingContext();
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
588 JS::Rooted
<JS::Value
> subframe(cx
);
590 if (!GetSubframeWindow(cx
, proxy
, id
, &subframe
, found
)) {
596 desc
.set(Some(JS::PropertyDescriptor::Data(
598 JS::PropertyAttribute::Configurable
,
599 JS::PropertyAttribute::Enumerable
,
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
609 if (!isSameOrigin
&& IsArrayIndex(GetArrayIndexFromId(id
))) {
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.
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
);
638 // See https://github.com/tc39/ecma262/issues/672 for more information.
640 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx
, id
)) {
641 (*desc
).setConfigurable(true);
646 // Now wrap our descriptor back into the Realm that asked for it.
647 return JS_WrapPropertyDescriptor(cx
, desc
);
651 if (!CrossOriginGetOwnPropertyHelper(cx
, proxy
, id
, desc
)) {
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
663 if (id
== GetJSIDByIndex(cx
, XPCJSContext::IDX_PRINT
)) {
664 if (!MaybeGetPDFJSPrintMethod(cx
, proxy
, desc
)) {
673 // Step 6 -- check for named subframes.
676 if (!name
.init(cx
, id
.toString())) {
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
)) {
685 desc
.set(Some(JS::PropertyDescriptor::Data(
686 childValue
, {JS::PropertyAttribute::Configurable
})));
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
);
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
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.
725 JS::Rooted
<Maybe
<JS::PropertyDescriptor
>> existingDesc(cx
);
726 ok
= js::Wrapper::getOwnPropertyDescriptor(cx
, proxy
, id
, &existingDesc
);
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.
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
);
747 if (!ourNewResult
.ok()) {
748 // Twiddling the configurable flag didn't help. Just return this failure
749 // out to the caller.
750 result
= ourNewResult
;
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();
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
)) {
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
)) {
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
)) {
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
)) {
810 // Add the "print" property if needed.
811 nsGlobalWindowOuter
* outer
= GetOuterWindow(proxy
);
812 nsGlobalWindowInner
* inner
=
813 nsGlobalWindowInner::Cast(outer
->GetCurrentInnerWindow());
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
)) {
826 return xpc::AppendCrossOriginWhitelistedPropNames(cx
, props
);
829 bool nsOuterWindowProxy::delete_(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
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
867 return hasOwn(cx
, proxy
, id
, bp
);
870 if (!GetSubframeWindow(cx
, proxy
, id
).IsNull()) {
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
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()) {
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
,
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
);
930 if (!GetSubframeWindow(cx
, proxy
, id
, vp
, found
)) {
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
945 JSAutoRealm
ar(cx
, proxy
);
947 JS_MarkCrossZoneId(cx
, id
);
949 JS::Rooted
<JS::Value
> wrappedReceiver(cx
, receiver
);
950 if (!MaybeWrapValue(cx
, &wrappedReceiver
)) {
954 // Fall through to js::Wrapper.
955 if (!js::Wrapper::get(cx
, proxy
, wrappedReceiver
, id
, vp
)) {
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
)) {
984 JS::Rooted
<JS::Value
> wrappedReceiver(cx
, receiver
);
985 if (!MaybeWrapValue(cx
, &wrappedReceiver
)) {
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
)) {
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.
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
)) {
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()) {
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
)) {
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
)) {
1066 for (int32_t i
= 0; i
< int32_t(length
); ++i
) {
1067 if (!props
.append(JS::PropertyKey::Int(i
))) {
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
);
1085 outerWindow
->UpdateWrapper(obj
, old
);
1086 BrowsingContext
* bc
= outerWindow
->GetBrowsingContext();
1088 bc
->UpdateWindowProxy(obj
, old
);
1094 enum { PDFJS_SLOT_CALLEE
= 0 };
1097 bool nsOuterWindowProxy::MaybeGetPDFJSPrintMethod(
1098 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
1099 JS::MutableHandle
<Maybe
<JS::PropertyDescriptor
>> desc
) {
1101 MOZ_ASSERT(!desc
.isSome());
1103 nsGlobalWindowOuter
* outer
= GetOuterWindow(proxy
);
1104 nsGlobalWindowInner
* inner
=
1105 nsGlobalWindowInner::Cast(outer
->GetCurrentInnerWindow());
1107 // No print method to expose.
1111 nsCOMPtr
<nsIPrincipal
> targetPrincipal
= GetNoPDFJSPrincipal(inner
);
1112 if (!targetPrincipal
) {
1113 // Nothing special to be done.
1117 if (!nsContentUtils::SubjectPrincipal(cx
)->Equals(targetPrincipal
)) {
1118 // Not our origin's PDF document.
1122 // Get the function we plan to actually call.
1123 JS::Rooted
<JSObject
*> innerObj(cx
, inner
->GetGlobalJSObject());
1125 // Really should not happen, but ok, let's just return.
1129 JS::Rooted
<JS::Value
> targetFunc(cx
);
1131 JSAutoRealm
ar(cx
, innerObj
);
1132 if (!JS_GetProperty(cx
, innerObj
, "print", &targetFunc
)) {
1137 if (!targetFunc
.isObject()) {
1138 // Who knows what's going on. Just return.
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
)) {
1153 js::NewFunctionWithReserved(cx
, PDFJSPrintMethod
, 0, 0, "print");
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
})));
1172 bool nsOuterWindowProxy::PDFJSPrintMethod(JSContext
* cx
, unsigned argc
,
1174 JS::CallArgs args
= CallArgsFromVp(argc
, vp
);
1176 JS::Rooted
<JSObject
*> realCallee(
1177 cx
, &js::GetFunctionNativeReserved(&args
.callee(), PDFJS_SLOT_CALLEE
)
1179 // Unchecked unwrap, because we want to extract the thing we really had
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
)) {
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
);
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
)) {
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
)) {
1229 // Don't bother passing through the args; they will get ignored anyway.
1231 if (!JS::Call(cx
, thisv
, realCallee
, JS::HandleValueArray::empty(),
1237 // Wrap the return value (not that there should be any!) into the right
1239 return MaybeWrapValue(cx
, args
.rval());
1243 already_AddRefed
<nsIPrincipal
> nsOuterWindowProxy::GetNoPDFJSPrincipal(
1244 nsGlobalWindowInner
* inner
) {
1245 if (!nsContentUtils::IsPDFJS(inner
->GetPrincipal())) {
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();
1260 const nsOuterWindowProxy
nsOuterWindowProxy::singleton
;
1262 class nsChromeOuterWindowProxy
: public nsOuterWindowProxy
{
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
,
1284 MOZ_ASSERT(JS_IsGlobalObject(global
));
1286 JSAutoRealm
ar(cx
, global
);
1288 js::WrapperOptions options
;
1289 options
.setClass(&OuterWindowProxyClass
);
1291 js::Wrapper::New(cx
, global
,
1292 isChrome
? &nsChromeOuterWindowProxy::singleton
1293 : &nsOuterWindowProxy::singleton
,
1295 MOZ_ASSERT_IF(obj
, js::IsWindowProxy(obj
));
1299 //*****************************************************************************
1300 //*** nsGlobalWindowOuter: Object Management
1301 //*****************************************************************************
1303 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID
)
1304 : nsPIDOMWindowOuter(aWindowID
),
1305 mFullscreenHasChangedDuringProcessing(false),
1306 mForceFullScreenInWidget(false),
1309 mHavePendingClose(false),
1310 mBlockScriptedClosingFlag(false),
1312 mCreatingInnerWindow(false),
1314 mAllowScriptsToClose(false),
1315 mTopLevelOuterContentWindow(false),
1316 mDelayedPrintUntilAfterLoad(false),
1317 mDelayedCloseForPrinting(false),
1318 mShouldDelayPrintUntilAfterLoad(false),
1321 mSetOpenerWindowCalled(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.
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
,
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);
1370 void nsGlobalWindowOuter::AssertIsOnMainThread() {
1371 MOZ_ASSERT(NS_IsMainThread());
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();
1396 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
)) {
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
);
1409 gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
,
1410 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1412 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1413 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial
,
1414 nullptr, url
.get()));
1418 MOZ_LOG(gDOMLeakPRLogOuter
, LogLevel::Debug
,
1419 ("DOMWINDOW %p destroyed", this));
1421 JSObject
* proxy
= GetWrapperMaybeDead();
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
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.
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
1455 MOZ_ASSERT(mCleanedUp
);
1457 nsCOMPtr
<nsIDeviceSensors
> ac
= do_GetService(NS_DEVICE_SENSORS_CONTRACTID
);
1458 if (ac
) ac
->RemoveWindowAsListener(this);
1460 nsLayoutStatics::Release();
1464 void nsGlobalWindowOuter::ShutDown() {
1465 AssertIsOnMainThread();
1467 delete sOuterWindowsById
;
1468 sOuterWindowsById
= nullptr;
1471 void nsGlobalWindowOuter::DropOuterWindowDocs() {
1472 MOZ_ASSERT_IF(mDoc
, !mDoc
->EventHandlingSuppressed());
1474 mSuspendedDocs
.Clear();
1477 void nsGlobalWindowOuter::CleanUp() {
1478 // Guarantee idempotence.
1479 if (mCleanedUp
) return;
1484 mWindowUtils
= nullptr;
1488 mContext
= nullptr; // Forces Release
1489 mChromeEventHandler
= nullptr; // Forces Release
1490 mParentTarget
= nullptr;
1491 mMessageManager
= nullptr;
1493 mArguments
= nullptr;
1496 void nsGlobalWindowOuter::ClearControllers() {
1499 mControllers
->GetControllerCount(&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
)) {
1540 tmp
->mCanSkipCCGeneration
= nsCCUncollectableMarker::sGeneration
;
1541 if (EventListenerManager
* elm
= tmp
->GetExistingListenerManager()) {
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())) {
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
);
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.
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
) {
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 {
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",
1681 OriginTrials
nsGlobalWindowOuter::Trials() const {
1682 return mInnerWindow
? nsGlobalWindowInner::Cast(mInnerWindow
)->Trials()
1686 FontFaceSet
* nsGlobalWindowOuter::GetFonts() {
1688 return mDoc
->Fonts();
1693 nsresult
nsGlobalWindowOuter::EnsureScriptEnvironment() {
1694 if (GetWrapperPreserveColor()) {
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);
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
) {
1725 if (!mDoc
->IsInitialDocument()) {
1731 nsCOMPtr
<nsIURI
> uri
;
1732 NS_GetURIWithoutRef(mDoc
->GetDocumentURI(), getter_AddRefs(uri
));
1733 NS_ASSERTION(NS_IsAboutBlank(uri
), "How'd this happen?");
1737 // Great, we're the original document, check for one of the other
1740 if (mDoc
== aNewDocument
) {
1744 if (aNewDocument
->IsStaticDocument()) {
1748 if (BasePrincipal::Cast(mDoc
->NodePrincipal())
1749 ->FastEqualsConsideringDomain(aNewDocument
->NodePrincipal())) {
1750 // The origin is the same.
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
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:
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;
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
1782 bool isNullPrincipal
;
1783 MOZ_ASSERT(NS_SUCCEEDED(mDoc
->NodePrincipal()->GetIsNullPrincipal(
1784 &isNullPrincipal
)) &&
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
,
1794 /* aIsInitialDocument */ true, aCOEP
);
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
{
1818 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID
)
1821 explicit WindowStateHolder(nsGlobalWindowInner
* aWindow
);
1823 nsGlobalWindowInner
* GetInnerWindow() { return mInnerWindow
; }
1825 void DidRestoreWindow() {
1826 mInnerWindow
= nullptr;
1827 mInnerWindowReflector
= nullptr;
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");
1848 // When a global goes into the bfcache, we disable script.
1849 xpc::Scriptability::Get(mInnerWindowReflector
).SetWindowAllowsScript(false);
1852 WindowStateHolder::~WindowStateHolder() {
1854 // This window was left in the bfcache and is now going away. We need to
1856 // Note that FreeInnerObjects may already have been called on the
1857 // inner window if its outer has already had SetDocShell(null)
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()) {
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
) {
1886 if (nsContentUtils::HttpsStateIsModern(aDocument
)) {
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
1894 nsCOMPtr
<nsIPrincipal
> precursorPrin
= principal
->GetPrecursorPrincipal();
1895 nsCOMPtr
<nsIURI
> uri
= precursorPrin
? precursorPrin
->GetURI() : nullptr;
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
)) {
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);
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
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
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();
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()) {
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
) {
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
;
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 "
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
);
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
;
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);
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
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();
2170 mLastOpenedURI
= aDocument
->GetDocumentURI();
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
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
);
2216 bool sameOrigin
= false;
2217 nsIPrincipal
* existing
= nsJSPrincipals::get(JS::GetRealmPrincipals(realm
));
2218 aDocument
->NodePrincipal()->Equals(existing
, &sameOrigin
);
2219 MOZ_ASSERT(sameOrigin
);
2221 JS::SetRealmPrincipals(realm
,
2222 nsJSPrincipals::get(aDocument
->NodePrincipal()));
2225 newInnerWindow
= wsh
->GetInnerWindow();
2226 newInnerGlobal
= newInnerWindow
->GetWrapper();
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
2258 rv
= CreateNativeGlobalForInner(
2259 cx
, newInnerWindow
, aDocument
, &newInnerGlobal
,
2260 ComputeIsSecureContext(aDocument
),
2261 newInnerWindow
->IsSharedMemoryAllowedInternal(
2262 aDocument
->NodePrincipal()));
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());
2302 JS::Rooted
<JSObject
*> outerObject(
2303 cx
, NewOuterWindowProxy(cx
, newInnerGlobal
, thisChrome
));
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
);
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();
2350 wc
? wc
->CanExecuteScripts() : mBrowsingContext
->CanExecuteScripts();
2351 xpc::Scriptability::Get(GetWrapperPreserveColor())
2352 .SetWindowAllowsScript(allow
);
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());
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");
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.
2405 MOZ_RELEASE_ASSERT(newInnerWindow
->mDoc
== aDocument
);
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();
2425 if (reUseInnerWindow
) {
2426 // The StorageAccess state may have changed. Invalidate the cached
2427 // StorageAllowed field, so that the next call to StorageAllowedForWindow
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
);
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
);
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
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
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
= [&] {
2508 RefPtr
<BrowsingContext
> bc
= mDocShell
->GetBrowsingContext();
2509 if (!bc
|| bc
->GetType() != BrowsingContext::Type::Chrome
) {
2513 return !mDoc
->NodePrincipal()->IsSystemPrincipal();
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();
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
);
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();
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
,
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()) {
2583 if (IsChromeWindow()) {
2587 nsIPrincipal
* principal
= GetPrincipal();
2588 nsIPrincipal
* storagePrincipal
= GetEffectiveStoragePrincipal();
2589 if (!principal
|| !storagePrincipal
) {
2595 nsCOMPtr
<nsIDOMStorageManager
> storageManager
=
2596 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv
);
2597 if (NS_FAILED(rv
)) {
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() {
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",
2640 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u
""_ns
); }
2642 void nsGlobalWindowOuter::SetDocShell(nsDocShell
* aDocShell
) {
2643 MOZ_ASSERT(aDocShell
);
2645 if (aDocShell
== mDocShell
) {
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
2671 nsCOMPtr
<nsPIDOMWindowOuter
> parentWindow
= GetInProcessParent();
2672 if (parentWindow
.get() != this) {
2673 mChromeEventHandler
= parentWindow
->GetChromeEventHandler();
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
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
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);
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();
2725 mChromeEventHandler
= nullptr; // force release now
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
)
2734 : GetWrapperPreserveColor());
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();
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
2764 nsCOMPtr
<Element
> frameElement
= GetFrameElementInternal();
2765 mMessageManager
= nsContentUtils::TryGetBrowserChildGlobal(frameElement
);
2767 if (!mMessageManager
) {
2768 nsGlobalWindowOuter
* topWin
= GetInProcessScriptableTopInternal();
2770 frameElement
= topWin
->GetFrameElementInternal();
2771 mMessageManager
= nsContentUtils::TryGetBrowserChildGlobal(frameElement
);
2775 if (!mMessageManager
) {
2777 nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler
);
2780 if (mMessageManager
) {
2781 mParentTarget
= mMessageManager
;
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();
2805 return group
->DialogsAreBeingAbused();
2808 bool nsGlobalWindowOuter::AreDialogsEnabled() {
2809 BrowsingContextGroup
* group
= mBrowsingContext
->Group();
2811 NS_ERROR("AreDialogsEnabled() called without a browsing context group?");
2815 // Dialogs are blocked if the content viewer is hidden
2817 nsCOMPtr
<nsIDocumentViewer
> viewer
;
2818 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
2821 viewer
->GetIsHidden(&isHidden
);
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
)) {
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");
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
) {
2869 void nsGlobalWindowOuter::DisableDialogs() {
2870 BrowsingContextGroup
* group
= mBrowsingContext
->Group();
2872 NS_ERROR("DisableDialogs() called without a browsing context group?");
2877 group
->SetAreDialogsEnabled(false);
2881 void nsGlobalWindowOuter::EnableDialogs() {
2882 BrowsingContextGroup
* group
= mBrowsingContext
->Group();
2884 NS_ERROR("EnableDialogs() called without a browsing context 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()) {
2903 nsresult
nsGlobalWindowOuter::SetArguments(nsIArray
* aArguments
) {
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
);
2920 //*****************************************************************************
2921 // nsGlobalWindowOuter::nsIScriptObjectPrincipal
2922 //*****************************************************************************
2924 nsIPrincipal
* nsGlobalWindowOuter::GetPrincipal() {
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());
2944 return objPrincipal
->GetPrincipal();
2950 nsIPrincipal
* nsGlobalWindowOuter::GetEffectiveCookiePrincipal() {
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());
2967 return objPrincipal
->GetEffectiveCookiePrincipal();
2973 nsIPrincipal
* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
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());
2990 return objPrincipal
->GetEffectiveStoragePrincipal();
2996 nsIPrincipal
* nsGlobalWindowOuter::PartitionedPrincipal() {
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());
3013 return objPrincipal
->PartitionedPrincipal();
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()) {
3043 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
3044 ("nsPIDOMWindowOuter, ActiveMediaComponents, "
3045 "no longer to delay media from start, this = %p\n",
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();
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();
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() {
3106 if (BrowsingContext
* parentBC
= GetBrowsingContext()->GetParent()) {
3107 if (nsCOMPtr
<nsPIDOMWindowOuter
> parent
= parentBC
->GetDOMWindow()) {
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() {
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
) {
3141 MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames
, !aScriptable
);
3143 // Walk up the parent chain.
3145 nsCOMPtr
<nsPIDOMWindowOuter
> prevParent
= aWin
;
3146 nsCOMPtr
<nsPIDOMWindowOuter
> parent
= aWin
;
3152 prevParent
= parent
;
3155 parent
= parent
->GetInProcessScriptableParent();
3157 parent
= parent
->GetInProcessParent();
3160 if (aExcludingExtensionAccessibleContentFrames
) {
3161 if (auto* p
= nsGlobalWindowOuter::Cast(parent
)) {
3162 nsGlobalWindowInner
* currentInner
= GetCurrentInnerWindowInternal(p
);
3163 nsIURI
* uri
= prevParent
->GetDocumentURI();
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
;
3197 } while (parent
!= prevParent
);
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
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()) {
3258 aRetval
.set(nullptr);
3262 JS::Rooted
<JS::Value
> val(aCx
);
3263 if (!ToJSValue(aCx
, WindowProxyHolder
{content
}, &val
)) {
3264 aError
.Throw(NS_ERROR_UNEXPECTED
);
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
3285 if (XRE_IsParentProcess() && aCallerType
== CallerType::System
) {
3286 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
= GetTreeOwner();
3288 aError
.Throw(NS_ERROR_FAILURE
);
3292 nsCOMPtr
<nsIDocShellTreeItem
> primaryContent
;
3293 treeOwner
->GetPrimaryContentShell(getter_AddRefs(primaryContent
));
3294 if (!primaryContent
) {
3298 return do_AddRef(primaryContent
->GetBrowsingContext());
3301 // For legacy untrusted callers we always return the same value as
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
);
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(
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
]);
3341 nsIControllers
* nsGlobalWindowOuter::GetControllersOuter(ErrorResult
& aError
) {
3342 if (!mControllers
) {
3343 mControllers
= new nsXULControllers();
3344 if (!mControllers
) {
3345 aError
.Throw(NS_ERROR_FAILURE
);
3349 // Add in the default controller
3350 RefPtr
<nsBaseCommandController
> commandController
=
3351 nsBaseCommandController::CreateWindowController();
3352 if (!commandController
) {
3353 aError
.Throw(NS_ERROR_FAILURE
);
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()) {
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());
3382 openerWin
->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
3387 return opener
.forget();
3390 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetSameProcessOpener() {
3391 if (RefPtr
<BrowsingContext
> opener
= GetOpenerBrowsingContext()) {
3392 return opener
->GetDOMWindow();
3397 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::GetOpenerWindowOuter() {
3398 if (RefPtr
<BrowsingContext
> opener
= GetOpenerBrowsingContext()) {
3399 return WindowProxyHolder(std::move(opener
));
3404 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::GetOpener() {
3405 return GetOpenerWindowOuter();
3408 void nsGlobalWindowOuter::GetStatusOuter(nsAString
& aStatus
) {
3412 void nsGlobalWindowOuter::SetStatusOuter(const nsAString
& aStatus
) {
3415 // We don't support displaying window.status in the UI, so there's nothing
3419 void nsGlobalWindowOuter::GetNameOuter(nsAString
& aName
) {
3421 mDocShell
->GetName(aName
);
3425 void nsGlobalWindowOuter::SetNameOuter(const nsAString
& aName
,
3426 mozilla::ErrorResult
& aError
) {
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
3438 CSSToLayoutDeviceScale
nsGlobalWindowOuter::CSSToDevScaleForBaseWindow(
3439 nsIBaseWindow
* aWindow
) {
3440 MOZ_ASSERT(aWindow
);
3441 auto scale
= aWindow
->UnscaledDevicePixelsPerCSSPixel();
3442 if (mBrowsingContext
) {
3443 scale
.scale
*= mBrowsingContext
->FullZoom();
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
) {
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
) {
3471 nsLayoutUtils::ExpandHeightForViewportUnits(presContext
, viewportSize
);
3474 aSize
= CSSPixel::FromAppUnits(viewportSize
);
3476 switch (StaticPrefs::dom_innerSize_rounding()) {
3478 aSize
.width
= std::roundf(aSize
.width
);
3479 aSize
.height
= std::roundf(aSize
.height
);
3482 aSize
.width
= std::truncf(aSize
.width
);
3483 aSize
.height
= std::truncf(aSize
.height
);
3492 double nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult
& aError
) {
3494 aError
= GetInnerSize(size
);
3498 nsresult
nsGlobalWindowOuter::GetInnerWidth(double* aInnerWidth
) {
3499 FORWARD_TO_INNER(GetInnerWidth
, (aInnerWidth
), NS_ERROR_UNEXPECTED
);
3502 double nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult
& aError
) {
3504 aError
= GetInnerSize(size
);
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
)) {
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.
3524 Maybe
<CSSIntSize
> deviceSize
= GetRDMDeviceSize(*mDoc
);
3525 if (deviceSize
.isSome()) {
3530 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
3531 if (!treeOwnerAsWin
) {
3532 aError
.Throw(NS_ERROR_FAILURE
);
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
)) {
3554 RefPtr
<nsPresContext
> pc
= mDocShell
->GetPresContext();
3555 if (NS_WARN_IF(!pc
)) {
3558 nsCOMPtr
<nsIWidget
> widget
= GetMainWidget();
3559 if (NS_WARN_IF(!widget
)) {
3562 LayoutDeviceIntPoint pt
= widget
->GetScreenEdgeSlop();
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();
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() {
3607 EnsureSizeAndPositionUpToDate();
3613 PresShell
* presShell
= mDocShell
->GetPresShell();
3617 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
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()
3641 if (bc
&& bc
->InRDMPane()) {
3642 nsIDocShell
* docShell
= topInProcessContentDoc
->GetDocShell();
3644 nsPresContext
* presContext
= docShell
->GetPresContext();
3646 nsCOMPtr
<nsIBrowserChild
> child
= docShell
->GetBrowserChild();
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
)));
3663 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType
) {
3664 // When resisting fingerprinting, always return 0.
3665 if (nsIGlobalObject::ShouldResistFingerprinting(
3666 aCallerType
, RFPTarget::WindowInnerScreenXY
)) {
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
)) {
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
,
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
) {
3706 if (aWidth
&& *aWidth
< 100) {
3709 if (aHeight
&& *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();
3774 int32_t screenTop
= screen
->AvailTop();
3778 if (screenLeft
+ screenWidth
< *aLeft
+ winRect
.width
)
3779 *aLeft
= screenLeft
+ screenWidth
- winRect
.width
;
3780 if (screenLeft
> *aLeft
) *aLeft
= screenLeft
;
3783 if (screenTop
+ screenHeight
< *aTop
+ winRect
.height
)
3784 *aTop
= screenTop
+ screenHeight
- winRect
.height
;
3785 if (screenTop
> *aTop
) *aTop
= screenTop
;
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
));
3803 CSSPoint
nsGlobalWindowOuter::GetScrollXY(bool aDoFlush
) {
3805 FlushPendingNotifications(FlushType::Layout
);
3807 EnsureSizeAndPositionUpToDate();
3810 nsIScrollableFrame
* sf
= GetScrollFrame();
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
);
3859 nsContentUtils::DispatchTrustedEvent(mDoc
, this, aEventName
,
3860 CanBubble::eYes
, Cancelable::eYes
,
3861 &defaultActionEnabled
);
3864 return defaultActionEnabled
;
3867 bool nsGlobalWindowOuter::DispatchResizeEvent(const CSSIntSize
& aSize
) {
3869 RefPtr
<Event
> domEvent
=
3870 mDoc
->CreateEvent(u
"CustomEvent"_ns
, CallerType::System
, res
);
3875 // We don't init the AutoJSAPI with ourselves because we don't want it
3876 // reporting errors to our onerror handlers.
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
)) {
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
);
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();
3938 PresShell
* presShell
= docShell
->GetPresShell();
3942 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
3946 return rootFrame
->GetView()->GetNearestWidget(nullptr);
3949 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen
,
3950 mozilla::ErrorResult
& aError
) {
3952 SetFullscreenInternal(FullscreenReason::ForFullscreenMode
, aFullscreen
);
3955 nsresult
nsGlobalWindowOuter::SetFullScreen(bool aFullscreen
) {
3956 return SetFullscreenInternal(FullscreenReason::ForFullscreenMode
,
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
);
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
{
3997 FullscreenTransitionTask(const FullscreenTransitionDuration
& aDuration
,
3998 nsGlobalWindowOuter
* aWindow
, bool aFullscreen
,
3999 nsIWidget
* aWidget
, nsISupports
* aTransitionData
)
4000 : mozilla::Runnable("FullscreenTransitionTask"),
4003 mTransitionData(aTransitionData
),
4004 mDuration(aDuration
),
4005 mStage(eBeforeToggle
),
4006 mFullscreen(aFullscreen
) {}
4008 NS_IMETHOD
Run() override
;
4011 ~FullscreenTransitionTask() override
= default;
4014 * The flow of fullscreen transition:
4016 * parent process | child process
4017 * ----------------------------------------------------------------
4019 * | request/exit fullscreen
4021 * BeforeToggle stage |
4023 * ToggleFullscreen stage *1 |----->
4024 * | HandleFullscreenRequests
4026 * <-----| MozAfterPaint event
4027 * AfterToggle stage *2 |
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.
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.
4039 // ToggleFullscreen stage actually executes the fullscreen toggle,
4040 // and wait for the next paint on the content to continue.
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.
4046 // End stage is triggered after the final transition finishes.
4050 class Observer final
: public nsIObserver
, public nsINamed
{
4056 explicit Observer(FullscreenTransitionTask
* aTask
) : mTask(aTask
) {}
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
;
4077 const char* const FullscreenTransitionTask::kPaintedTopic
=
4078 "fullscreen-painted";
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;
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
,
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.
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
,
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();
4152 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer
, nsIObserver
, nsINamed
)
4155 FullscreenTransitionTask::Observer::Observe(nsISupports
* aSubject
,
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
);
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");
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;
4190 FullscreenTransitionTask::Observer::GetName(nsACString
& aName
) {
4191 aName
.AssignLiteral("FullscreenTransitionTask");
4195 static bool MakeWidgetFullscreen(nsGlobalWindowOuter
* aWindow
,
4196 FullscreenReason aReason
, bool aFullscreen
) {
4197 nsCOMPtr
<nsIWidget
> widget
= aWindow
->GetMainWidget();
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
);
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.
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
);
4261 nsresult
nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason
,
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
);
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()) {
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
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
4316 MOZ_ASSERT_IF(aFullscreen
&& aReason
== FullscreenReason::ForFullscreenMode
,
4317 mFullscreen
.value() != FullscreenReason::ForFullscreenAPI
);
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
;
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);
4348 // Set this before so if widget sends an event indicating its
4349 // gone full screen, the state trap above works.
4351 mFullscreen
.emplace(aReason
);
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;
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
,
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();
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
);
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
,
4411 if (mFullscreen
.isSome() != aIsFullscreen
) {
4412 if (aIsFullscreen
) {
4413 mFullscreen
.emplace(FullscreenReason::ForFullscreenMode
);
4415 mFullscreen
.reset();
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
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
);
4430 DispatchCustomEvent(u
"willexitfullscreen"_ns
, ChromeOnlyDispatch::eYes
);
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();
4447 MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?");
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
);
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()) {
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
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());
4497 void nsGlobalWindowOuter::MacFullscreenMenubarOverlapChanged(
4498 mozilla::DesktopCoord aOverlapAmount
) {
4500 RefPtr
<Event
> domEvent
=
4501 mDoc
->CreateEvent(u
"CustomEvent"_ns
, CallerType::System
, res
);
4508 JSContext
* cx
= jsapi
.cx();
4509 JSAutoRealm
ar(cx
, GetWrapperPreserveColor());
4511 JS::Rooted
<JS::Value
> detailValue(cx
);
4512 if (!ToJSValue(cx
, aOverlapAmount
, &detailValue
)) {
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
;
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 "
4566 if (!mDocShell
) return;
4568 RefPtr
<PresShell
> presShell
= mDocShell
->GetPresShell();
4573 // Flush pending reflows.
4575 mDoc
->FlushPendingNotifications(FlushType::Layout
);
4578 // Unsuppress painting.
4579 presShell
->UnsuppressPainting();
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
);
4600 auto* addonPolicy
= BasePrincipal::Cast(aSubjectPrincipal
)->AddonPolicy();
4602 nsContentUtils::FormatLocalizedString(
4603 aOutMessage
, nsContentUtils::eCOMMON_DIALOG_PROPERTIES
,
4604 aContentMessage
, addonPolicy
->Name());
4606 nsresult rv
= NS_ERROR_FAILURE
;
4608 nsCOMPtr
<nsIURI
> uri
= aSubjectPrincipal
->GetURI();
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
,
4633 if (aOutMessage
.IsEmpty()) {
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
4645 if (!mBrowsingContext
->HadOriginalOpener()) {
4649 if (!CanSetProperty("dom.disable_window_move_resize")) {
4653 // Ignore the request if we have more than one tab in the window.
4654 if (mBrowsingContext
->Top()->HasSiblings()) {
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");
4670 nsGlobalWindowInner::sDragServiceDisabled
= 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.
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
4696 EnsureReflowFlushAndPaint();
4699 MakeMessageWithPrincipal(title
, &aSubjectPrincipal
, false,
4700 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
4701 "ScriptDlgGenericHeading");
4703 // Remove non-terminating null characters from the
4704 // string. See bug #310037.
4706 nsContentUtils::StripNullChars(aMessage
, final
);
4707 nsContentUtils::PlatformToDOMLineBreaks(final
);
4710 nsCOMPtr
<nsIPromptFactory
> promptFac
=
4711 do_GetService("@mozilla.org/prompter;1", &rv
);
4712 if (NS_FAILED(rv
)) {
4717 nsCOMPtr
<nsIPrompt
> prompt
;
4719 promptFac
->GetPrompt(this, NS_GET_IID(nsIPrompt
), getter_AddRefs(prompt
));
4720 if (aError
.Failed()) {
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;
4735 MakeMessageWithPrincipal(
4736 label
, &aSubjectPrincipal
, true, "ScriptDialogLabelNullPrincipal",
4737 "ScriptDialogLabelContentPrincipal", "ScriptDialogLabelNullPrincipal");
4740 ? prompt
->AlertCheck(title
.get(), final
.get(), label
.get(),
4742 : prompt
->ConfirmCheck(title
.get(), final
.get(), label
.get(),
4743 &disallowDialog
, &result
);
4745 if (disallowDialog
) {
4749 aError
= aAlert
? prompt
->Alert(title
.get(), final
.get())
4750 : prompt
->Confirm(title
.get(), final
.get(), &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
,
4769 void nsGlobalWindowOuter::PromptOuter(const nsAString
& aMessage
,
4770 const nsAString
& aInitial
,
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.
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
4790 EnsureReflowFlushAndPaint();
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
);
4805 nsCOMPtr
<nsIPromptFactory
> promptFac
=
4806 do_GetService("@mozilla.org/prompter;1", &rv
);
4807 if (NS_FAILED(rv
)) {
4812 nsCOMPtr
<nsIPrompt
> prompt
;
4814 promptFac
->GetPrompt(this, NS_GET_IID(nsIPrompt
), getter_AddRefs(prompt
));
4815 if (aError
.Failed()) {
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;
4830 label
.SetIsVoid(true);
4831 if (ShouldPromptToBlockDialogs()) {
4832 nsContentUtils::GetLocalizedString(
4833 nsContentUtils::eCOMMON_DIALOG_PROPERTIES
, "ScriptDialogLabel", label
);
4836 nsAutoSyncOperation
sync(mDoc
, SyncOperationBehavior::eSuspendInput
);
4838 aError
= prompt
->Prompt(title
.get(), fixedMessage
.get(), &inoutValue
,
4839 label
.IsVoid() ? nullptr : label
.get(),
4840 &disallowDialog
, &ok
);
4842 if (disallowDialog
) {
4846 // XXX Doesn't this leak inoutValue?
4847 if (aError
.Failed()) {
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
)) {
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.");
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");
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();
4902 parent
= bc
->GetParent();
4903 if (!parent
&& XRE_IsParentProcess()) {
4904 parent
= bc
->Canonical()->GetParentCrossChromeBoundary();
4908 if (!parent
->IsInProcess()) {
4910 OwningNonNull
<nsGlobalWindowOuter
> kungFuDeathGrip(*this);
4911 fm
->WindowRaised(kungFuDeathGrip
, aActionId
);
4913 ContentChild
* contentChild
= ContentChild::GetSingleton();
4914 MOZ_ASSERT(contentChild
);
4915 contentChild
->SendFinalizeFocusOuter(bc
, canFocus
, aCallerType
);
4920 MOZ_ASSERT(mDoc
, "Call chain should have ensured document creation.");
4922 if (Element
* frame
= mDoc
->GetEmbedderElement()) {
4923 nsContentUtils::RequestFrameFocus(*frame
, canFocus
, aCallerType
);
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
)) {
4947 nsCOMPtr
<nsIWebBrowserChrome
> chrome
= GetWebBrowserChrome();
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()) {
4961 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(mDocShell
));
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
4973 // Printing is disabled, silently return.
4974 if (!StaticPrefs::print_enabled()) {
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;
4991 RefPtr
<BrowsingContext
> top
=
4992 mBrowsingContext
? mBrowsingContext
->Top() : nullptr;
4993 if (NS_WARN_IF(top
&& top
->GetIsPrinting())) {
4998 Unused
<< top
->SetIsPrinting(true);
5001 auto unset
= MakeScopeExit([&] {
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
);
5013 class MOZ_RAII AutoModalState
{
5015 explicit AutoModalState(nsGlobalWindowOuter
& aWin
)
5016 : mModalStateWin(aWin
.EnterModalState()) {}
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
) {
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");
5042 nsCOMPtr
<nsIPrintSettings
> ps
= aPrintSettings
;
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");
5055 RefPtr
<BrowsingContext
> sourceBC
= docToPrint
->GetBrowsingContext();
5056 MOZ_DIAGNOSTIC_ASSERT(sourceBC
);
5058 aError
.ThrowNotSupportedError("No browsing context for source document");
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?");
5091 if (usingCachedBrowsingContext
) {
5092 bc
= docToPrint
->GetBrowsingContext();
5094 // We're already a print preview window, just reuse our browsing context /
5098 nsCOMPtr
<nsIDocShell
> docShell
= bc
->GetDocShell();
5100 aError
.ThrowNotSupportedError("No docshell");
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 "
5110 docShell
->GetDocViewer(getter_AddRefs(viewer
));
5111 MOZ_DIAGNOSTIC_ASSERT(viewer
);
5113 if (aDocShellToCloneInto
) {
5114 // Ensure the content viewer is created if needed.
5115 Unused
<< aDocShellToCloneInto
->GetDocument();
5116 bc
= aDocShellToCloneInto
->GetBrowsingContext();
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
,
5128 false, // aContentModal
5129 true, // aCalledNoScript
5130 false, // aDoJSFixups
5132 nullptr, nullptr, // No args
5133 nullptr, // aLoadState
5134 false, // aForceNoOpener
5135 printKind
, getter_AddRefs(bc
));
5136 if (NS_WARN_IF(aError
.Failed())) {
5139 if (aCachedBrowsingContext
) {
5140 MOZ_ASSERT(!*aCachedBrowsingContext
);
5141 *aCachedBrowsingContext
= bc
;
5145 aError
.ThrowNotAllowedError("No browsing context");
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
);
5155 aError
.ThrowNotSupportedError("Didn't end up with a content viewer");
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
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
);
5187 aError
.ThrowNotSupportedError("Clone operation for printing failed");
5192 nsCOMPtr
<nsIWebBrowserPrint
> webBrowserPrint
= do_QueryInterface(viewer
);
5193 if (!webBrowserPrint
) {
5194 aError
.ThrowNotSupportedError(
5195 "Content viewer didn't implement nsIWebBrowserPrint");
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
5206 closeWindowAfterPrint
= usingCachedBrowsingContext
;
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()) {
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
) {
5245 if (aIsPreview
== IsPreview::Yes
) {
5246 return !hasPrintCallbacks
;
5248 return StaticPrefs::dom_window_print_fuzzing_block_while_printing();
5252 SpinEventLoopUntil("nsGlobalWindowOuter::Print"_ns
,
5253 [&] { return bc
->IsDiscarded(); });
5256 return WindowProxyHolder(std::move(bc
));
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()) {
5274 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5275 if (!treeOwnerAsWin
) {
5276 aError
.Throw(NS_ERROR_FAILURE
);
5280 // We need to do the same transformation GetScreenXY does.
5281 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
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()) {
5310 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5311 if (!treeOwnerAsWin
) {
5312 aError
.Throw(NS_ERROR_FAILURE
);
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.
5321 aError
= treeOwnerAsWin
->GetPosition(&x
, &y
);
5322 if (aError
.Failed()) {
5326 auto cssScale
= CSSToDevScaleForBaseWindow(treeOwnerAsWin
);
5327 CSSIntPoint cssPos
= RoundedToInt(treeOwnerAsWin
->GetPosition() / cssScale
);
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
) {
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()) {
5359 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5360 if (!treeOwnerAsWin
) {
5361 aError
.Throw(NS_ERROR_FAILURE
);
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()) {
5387 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5388 if (!treeOwnerAsWin
) {
5389 aError
.Throw(NS_ERROR_FAILURE
);
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
) {
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()) {
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
));
5435 return aError
.Throw(NS_ERROR_FAILURE
);
5438 auto contentSize
= viewer
->GetContentSize(
5439 aConstraints
.mMaxWidth
, aConstraints
.mMaxHeight
, aConstraints
.mPrefWidth
);
5441 return aError
.Throw(NS_ERROR_FAILURE
);
5444 // Make sure the new size is following the CheckSecurityWidthAndHeight
5446 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
= GetTreeOwner();
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();
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
;
5468 treeOwner
->SizeShellTo(docShell
, newDevSize
.width
, newDevSize
.height
);
5471 already_AddRefed
<nsPIWindowRoot
> nsGlobalWindowOuter::GetTopWindowRoot() {
5472 nsPIDOMWindowOuter
* piWin
= GetPrivateRoot();
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
) {
5487 // Fire a "DOMPopupBlocked" event so that the UI can hear about
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
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
);
5508 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName
) {
5509 // Chrome can set any property.
5510 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
5514 // If the pref is set to true, we can not set the property
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
));
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
);
5559 // XXX Is it possible that some internal errors are thrown here?
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
,
5576 false, // aContentModal
5577 true, // aCalledNoScript
5578 false, // aDoJSFixups
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
,
5590 false, // aContentModal
5591 false, // aCalledNoScript
5592 true, // aDoJSFixups
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
,
5609 false, // aContentModal
5610 true, // aCalledNoScript
5611 false, // aDoJSFixups
5613 nullptr, aExtraArgument
, // Arguments
5614 nullptr, // aLoadState
5615 false, // aForceNoOpener
5616 PrintKind::None
, _retval
);
5619 // Like Open, but passes aNavigate=false.
5621 nsresult
nsGlobalWindowOuter::OpenNoNavigate(const nsAString
& aUrl
,
5622 const nsAString
& aName
,
5623 const nsAString
& aOptions
,
5624 BrowsingContext
** _retval
) {
5625 return OpenInternal(aUrl
, aName
, aOptions
,
5627 false, // aContentModal
5628 true, // aCalledNoScript
5629 false, // aDoJSFixups
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
;
5643 NS_CreateJSArgv(aCx
, aExtraArgument
.Length(), aExtraArgument
.Elements(),
5644 getter_AddRefs(argvArray
));
5645 if (aError
.Failed()) {
5649 RefPtr
<BrowsingContext
> dialog
;
5650 aError
= OpenInternal(aUrl
, aName
, aOptions
,
5652 false, // aContentModal
5653 false, // aCalledNoScript
5654 false, // aDoJSFixups
5656 argvArray
, nullptr, // Arguments
5657 nullptr, // aLoadState
5658 false, // aForceNoOpener
5659 PrintKind::None
, getter_AddRefs(dialog
));
5663 return WindowProxyHolder(std::move(dialog
));
5666 WindowProxyHolder
nsGlobalWindowOuter::GetFramesOuter() {
5667 RefPtr
<nsPIDOMWindowOuter
> frames(this);
5668 FlushPendingNotifications(FlushType::ContentAndNotify
);
5669 return WindowProxyHolder(mBrowsingContext
);
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();
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();
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();
5712 BasePrincipal::Cast(callerPrin
)->GetScriptLocation(*aScriptLocation
);
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
) {
5728 // otherwise use the URI of the document to generate origin
5729 nsContentUtils::GetWebExposedOriginSerialization(*aCallerURI
, aOrigin
);
5731 // in case of a sandbox with a system principal origin can be empty
5732 if (!callerPrin
->IsSystemPrincipal()) {
5736 NS_IF_ADDREF(*aCallerPrincipal
= callerPrin
);
5738 // "/" indicates same origin as caller, "*" indicates no specific origin is
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
);
5747 nsresult rv
= NS_MutateURI(targetOriginURI
)
5749 .SetPathQueryRef(""_ns
)
5750 .Finalize(aTargetOriginURI
);
5751 if (NS_FAILED(rv
)) {
5756 if (!nsContentUtils::IsCallerChrome() && callerInnerWin
&&
5757 callerInnerWin
->GetOuterWindowInternal()) {
5758 NS_ADDREF(*aSource
= callerInnerWin
->GetOuterWindowInternal()
5759 ->GetBrowsingContext());
5764 if (aCallerAgentClusterId
&& callerInnerWin
&&
5765 callerInnerWin
->GetDocGroup()) {
5766 *aCallerAgentClusterId
=
5767 Some(callerInnerWin
->GetDocGroup()->AgentClusterId());
5770 callerInnerWin
.forget(aCallerInnerWindow
);
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");
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 )"
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
5827 BasePrincipal::CreateContentPrincipal(aTargetOriginURI
, attrs
);
5828 if (NS_WARN_IF(!providedPrincipal
)) {
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
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
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
) {
5859 providedPrincipal
.forget(aProvidedPrincipal
);
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
)) {
5885 nsCOMPtr
<nsIPrincipal
> providedPrincipal
;
5886 if (!GetPrincipalForPostMessage(aTargetOrigin
, targetOriginURI
,
5887 callerPrincipal
, aSubjectPrincipal
,
5888 getter_AddRefs(providedPrincipal
))) {
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())) {
5915 event
->DispatchToTargetThread(aError
);
5918 class nsCloseEvent
: public Runnable
{
5919 RefPtr
<nsGlobalWindowOuter
> mWindow
;
5922 nsCloseEvent(nsGlobalWindowOuter
* aWindow
, bool aIndirect
)
5923 : mozilla::Runnable("nsCloseEvent"),
5925 mIndirect(aIndirect
) {}
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
{
5936 return PostCloseEvent(mWindow
, false);
5938 mWindow
->ReallyCloseWindow();
5944 bool nsGlobalWindowOuter::CanClose() {
5946 nsCOMPtr
<nsIBrowserDOMWindow
> bwin
= GetBrowserDOMWindow();
5948 bool canClose
= true;
5949 if (bwin
&& NS_SUCCEEDED(bwin
->CanClose(&canClose
))) {
5958 nsCOMPtr
<nsIDocumentViewer
> viewer
;
5959 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
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;
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.
5983 if (mHavePendingClose
) {
5984 // We're going to be closed anyway; do nothing since we don't want
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.
5996 // Don't allow scripts from content to close non-neterror windows that
5997 // were not opened by script.
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()) {
6007 mAllowScriptsToClose
||
6008 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
6010 // We're blocking the close operation
6011 // report localized error msg in JS console
6012 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
6014 mDoc
, // Better name for the category?
6015 nsContentUtils::eDOM_PROPERTIES
,
6016 "WindowCloseByScriptBlockedWarning");
6023 if (!mInClose
&& !mIsClosed
&& !CanClose()) {
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
6034 bool wasInClose
= mInClose
;
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
;
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();
6065 nsresult
nsGlobalWindowOuter::Close() {
6066 CloseOuter(/* aTrustedCaller = */ true);
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.
6079 if (mHavePendingClose
) {
6080 // We're going to be closed anyway; do nothing since we don't want
6087 DispatchCustomEvent(u
"DOMWindowClose"_ns
, ChromeOnlyDispatch::eYes
);
6092 void nsGlobalWindowOuter::FinalClose() {
6093 // Flag that we were closed.
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
) {
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
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();
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
) {
6137 treeOwnerAsWin
->Destroy();
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();
6185 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
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");
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
)) {
6235 topWin
->mModalStateDepth
++;
6239 void nsGlobalWindowOuter::LeaveModalState() {
6241 nsGlobalWindowOuter
* topWin
= GetInProcessScriptableTopInternal();
6243 NS_WARNING("Uh, LeaveModalState() called w/o a reachable top window?");
6247 if (topWin
!= this) {
6248 MOZ_ASSERT(IsSuspended());
6249 return topWin
->LeaveModalState();
6253 MOZ_ASSERT(mModalStateDepth
!= 0);
6254 MOZ_ASSERT(IsSuspended());
6257 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
6258 if (mModalStateDepth
== 0) {
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();
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
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();
6305 if (!aSubjectPrincipal
.SubsumesConsideringDomain(element
->NodePrincipal())) {
6312 Element
* nsGlobalWindowOuter::GetFrameElement() {
6313 if (!mBrowsingContext
|| mBrowsingContext
->IsTop()) {
6316 return mBrowsingContext
->GetEmbedderElement();
6320 class ChildCommandDispatcher
: public Runnable
{
6322 ChildCommandDispatcher(nsPIWindowRoot
* aRoot
, nsIBrowserChild
* aBrowserChild
,
6323 nsPIDOMWindowOuter
* aWindow
, const nsAString
& aAction
)
6324 : mozilla::Runnable("ChildCommandDispatcher"),
6326 mBrowserChild(aBrowserChild
),
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
);
6343 nsCOMPtr
<nsPIWindowRoot
> mRoot
;
6344 nsCOMPtr
<nsIBrowserChild
> mBrowserChild
;
6345 nsCOMPtr
<nsPIDOMWindowOuter
> mWindow
;
6349 class CommandDispatcher
: public Runnable
{
6351 CommandDispatcher(nsIDOMXULCommandDispatcher
* aDispatcher
,
6352 const nsAString
& aAction
)
6353 : mozilla::Runnable("CommandDispatcher"),
6354 mDispatcher(aDispatcher
),
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
;
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();
6373 nsContentUtils::AddScriptRunner(
6374 new ChildCommandDispatcher(root
, child
, this, anAction
));
6380 nsPIDOMWindowOuter
* rootWindow
= GetPrivateRoot();
6385 Document
* doc
= rootWindow
->GetExtantDoc();
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() {
6405 PresShell
* presShell
= mDocShell
->GetPresShell();
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
));
6426 aError
.Throw(NS_ERROR_NOT_AVAILABLE
);
6430 // Set the options of the search
6431 aError
= finder
->SetSearchString(aString
);
6432 if (aError
.Failed()) {
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
));
6447 framesFinder
->SetRootSearchFrame(this); // paranoia
6448 framesFinder
->SetCurrentSearchFrame(this);
6451 if (aString
.IsEmpty()) {
6455 // Launch the search with the passed in search string
6456 bool didFind
= false;
6457 aError
= finder
->FindNext(&didFind
);
6461 //*****************************************************************************
6463 //*****************************************************************************
6465 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() {
6469 nsIGlobalObject
* nsGlobalWindowOuter::GetOwnerGlobal() const {
6470 return GetCurrentInnerWindowInternal(this);
6473 bool nsGlobalWindowOuter::DispatchEvent(Event
& aEvent
, CallerType aCallerType
,
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
));
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();
6514 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetPrivateRoot() {
6515 nsCOMPtr
<nsPIDOMWindowOuter
> top
= GetInProcessTop();
6517 nsCOMPtr
<nsIContent
> chromeElement(do_QueryInterface(mChromeEventHandler
));
6518 if (chromeElement
) {
6519 Document
* doc
= chromeElement
->GetComposedDoc();
6521 nsCOMPtr
<nsPIDOMWindowOuter
> parent
= doc
->GetWindow();
6523 top
= parent
->GetInProcessTop();
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
6557 inner
->ResetVRTelemetry(true);
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
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
,
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
,
6627 RefPtr
<nsICSSDeclaration
> compStyle
= NS_NewComputedDOMStyle(
6628 &aElt
, aPseudoElt
, mDoc
,
6629 aDefaultStylesOnly
? nsComputedDOMStyle::StyleType::DefaultOnly
6630 : nsComputedDOMStyle::StyleType::All
,
6633 return compStyle
.forget();
6636 //*****************************************************************************
6637 // nsGlobalWindowOuter::nsIInterfaceRequestor
6638 //*****************************************************************************
6640 nsresult
nsGlobalWindowOuter::GetInterfaceInternal(const nsIID
& aIID
,
6642 NS_ENSURE_ARG_POINTER(aSink
);
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
);
6653 else if (aIID
.Equals(NS_GET_IID(nsIWebBrowserPrint
))) {
6655 nsCOMPtr
<nsIDocumentViewer
> viewer
;
6656 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
6658 nsCOMPtr
<nsIWebBrowserPrint
> webBrowserPrint(do_QueryInterface(viewer
));
6659 webBrowserPrint
.forget(aSink
);
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
;
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
);
6681 bool nsGlobalWindowOuter::IsSuspended() const {
6682 MOZ_ASSERT(NS_IsMainThread());
6683 // No inner means we are effectively suspended
6684 if (!mInnerWindow
) {
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
) {
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) {
6718 void nsGlobalWindowOuter::UnblockScriptedClosing() {
6719 mBlockScriptedClosingFlag
= false;
6722 class AutoUnblockScriptClosing
{
6724 RefPtr
<nsGlobalWindowOuter
> mWin
;
6727 explicit AutoUnblockScriptClosing(nsGlobalWindowOuter
* aWin
) : mWin(aWin
) {
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
) {
6747 if (argv
) argv
->GetLength(&argc
);
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
);
6762 nsCOMPtr
<nsIWebBrowserChrome
> chrome
= GetWebBrowserChrome();
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
) {
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
&&
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.
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
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
));
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
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
;
6918 // Reset popup state while opening a window to prevent the
6919 // current state from being active the whole time a modal
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
));
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
) {
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
);
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();
6978 domReturn
.forget(aReturn
);
6982 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI
* aURI
) {
6983 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
6984 if (NS_WARN_IF(!inner
)) {
6988 // No 3rd party URL/window.
6989 if (!AntiTrackingUtils::IsThirdPartyWindow(inner
, aURI
)) {
6993 Document
* doc
= inner
->GetDoc();
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
);
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.
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.
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() {
7055 PresShell
* presShell
= mDocShell
->GetPresShell();
7057 return presShell
->GetRootScrollFrameAsScrollable();
7062 nsresult
nsGlobalWindowOuter::SecurityCheckURL(const char* aURL
,
7064 nsCOMPtr
<nsPIDOMWindowInner
> sourceWindow
=
7065 do_QueryInterface(GetEntryGlobal());
7066 if (!sourceWindow
) {
7067 sourceWindow
= GetCurrentInnerWindow();
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
7081 baseURI
= doc
->GetDocBaseURI();
7082 encoding
= doc
->GetDocumentCharacterSet();
7084 nsCOMPtr
<nsIURI
> uri
;
7085 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), nsDependentCString(aURL
),
7087 if (NS_WARN_IF(NS_FAILED(rv
))) {
7088 return NS_ERROR_DOM_SYNTAX_ERR
;
7091 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript(
7093 return NS_ERROR_FAILURE
;
7100 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType
) {
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.
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
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.
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);
7181 holder
->DidRestoreWindow();
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;
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...
7230 RefPtr
<nsPresContext
> presContext
;
7232 presContext
= mDocShell
->GetPresContext();
7236 // Need root widget.
7237 PresShell
* presShell
= mDocShell
->GetPresShell();
7239 aError
.Throw(NS_ERROR_FAILURE
);
7243 nsViewManager
* vm
= presShell
->GetViewManager();
7245 aError
.Throw(NS_ERROR_FAILURE
);
7249 nsView
* rootView
= vm
->GetRootView();
7251 aError
.Throw(NS_ERROR_FAILURE
);
7255 nsIWidget
* widget
= rootView
->GetNearestWidget(nullptr);
7257 aError
.Throw(NS_ERROR_FAILURE
);
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!");
7283 return GetCurrentInnerWindowInternal(this)->MessageManager();
7286 ChromeMessageBroadcaster
* nsGlobalWindowOuter::GetGroupMessageManager(
7287 const nsAString
& aGroup
) {
7288 if (!mInnerWindow
) {
7289 NS_WARNING("No inner window available!");
7292 return GetCurrentInnerWindowInternal(this)->GetGroupMessageManager(aGroup
);
7295 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline
= NS_IsOffline(); }
7297 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7299 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7300 # error "Never include unwrapped windows.h in this file!"
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() {
7308 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
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()) {
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()) {
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()) {
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())) {
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
7372 } else if (!info
->mForceMaybeResetName
.ref()) {
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();
7390 Unused
<< mBrowsingContext
->SetName(EmptyString());
7393 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
7394 BrowsingContext
* aBC
) {
7395 BrowsingContextGroup
* group
= aBC
->Group();
7398 "nsGlobalWindowOuter::TemporarilyDisableDialogs called without a "
7399 "browsing context group?");
7405 mSavedDialogsEnabled
= group
->GetAreDialogsEnabled();
7406 group
->SetAreDialogsEnabled(false);
7410 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() {
7412 mGroup
->SetAreDialogsEnabled(mSavedDialogsEnabled
);
7417 already_AddRefed
<nsGlobalWindowOuter
> nsGlobalWindowOuter::Create(
7418 nsDocShell
* aDocShell
, bool aIsChrome
) {
7419 uint64_t outerWindowID
= aDocShell
->GetOuterWindowID();
7420 RefPtr
<nsGlobalWindowOuter
> window
= new nsGlobalWindowOuter(outerWindowID
);
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() {
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();
7445 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal(
7446 EventTarget
* aChromeEventHandler
) {
7447 // Out-of-line so we don't need to include ContentFrameMessageManager.h in
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();
7460 return doc
->GetDocGroup();
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;