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/Result.h"
28 #include "mozilla/dom/AutoPrintEventDispatcher.h"
29 #include "mozilla/dom/BindingUtils.h"
30 #include "mozilla/dom/BrowserChild.h"
31 #include "mozilla/dom/BrowsingContextBinding.h"
32 #include "mozilla/dom/CanonicalBrowsingContext.h"
33 #include "mozilla/dom/ContentChild.h"
34 #include "mozilla/dom/ContentFrameMessageManager.h"
35 #include "mozilla/dom/DocumentInlines.h"
36 #include "mozilla/dom/EventTarget.h"
37 #include "mozilla/dom/HTMLIFrameElement.h"
38 #include "mozilla/dom/LocalStorage.h"
39 #include "mozilla/dom/LSObject.h"
40 #include "mozilla/dom/Storage.h"
41 #include "mozilla/dom/MaybeCrossOriginObject.h"
42 #include "mozilla/dom/Performance.h"
43 #include "mozilla/dom/ProxyHandlerUtils.h"
44 #include "mozilla/dom/StorageEvent.h"
45 #include "mozilla/dom/StorageEventBinding.h"
46 #include "mozilla/dom/StorageNotifierService.h"
47 #include "mozilla/dom/StorageUtils.h"
48 #include "mozilla/dom/Timeout.h"
49 #include "mozilla/dom/TimeoutHandler.h"
50 #include "mozilla/dom/TimeoutManager.h"
51 #include "mozilla/dom/UserActivation.h"
52 #include "mozilla/dom/WindowContext.h"
53 #include "mozilla/dom/WindowFeatures.h" // WindowFeatures
54 #include "mozilla/dom/WindowProxyHolder.h"
55 #include "mozilla/IntegerPrintfMacros.h"
56 #include "mozilla/StorageAccessAPIHelper.h"
57 #include "nsBaseCommandController.h"
59 #include "nsICookieService.h"
60 #include "nsISizeOfEventTarget.h"
61 #include "nsDOMJSUtils.h"
62 #include "nsArrayUtils.h"
63 #include "nsIDocShellTreeOwner.h"
64 #include "nsIInterfaceRequestorUtils.h"
65 #include "nsIPermissionManager.h"
66 #include "nsIScriptContext.h"
67 #include "nsWindowMemoryReporter.h"
68 #include "nsWindowSizes.h"
69 #include "nsWindowWatcher.h"
70 #include "WindowNamedPropertiesHandler.h"
71 #include "nsFrameSelection.h"
72 #include "nsNetUtil.h"
73 #include "nsVariant.h"
74 #include "nsPrintfCString.h"
75 #include "mozilla/intl/LocaleService.h"
76 #include "WindowDestroyedEvent.h"
77 #include "nsDocShellLoadState.h"
78 #include "mozilla/dom/WindowGlobalChild.h"
81 #include "nsJSUtils.h"
83 #include "jsfriendapi.h"
84 #include "js/CallAndConstruct.h" // JS::Call
85 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
86 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxy
87 #include "js/PropertyAndElement.h" // JS_DefineObject, JS_GetProperty
88 #include "js/PropertySpec.h"
89 #include "js/RealmIterators.h"
90 #include "js/Wrapper.h"
91 #include "nsLayoutUtils.h"
92 #include "nsReadableUtils.h"
93 #include "nsJSEnvironment.h"
94 #include "mozilla/dom/ScriptSettings.h"
95 #include "mozilla/Preferences.h"
96 #include "mozilla/Likely.h"
97 #include "mozilla/SchedulerGroup.h"
98 #include "mozilla/SpinEventLoopUntil.h"
99 #include "mozilla/Sprintf.h"
100 #include "mozilla/Unused.h"
103 #include "mozilla/dom/BarProps.h"
104 #include "nsLayoutStatics.h"
105 #include "nsCCUncollectableMarker.h"
106 #include "mozilla/dom/WorkerCommon.h"
107 #include "mozilla/dom/ToJSValue.h"
108 #include "nsJSPrincipals.h"
109 #include "mozilla/Attributes.h"
110 #include "mozilla/Components.h"
111 #include "mozilla/Debug.h"
112 #include "mozilla/EventListenerManager.h"
113 #include "mozilla/MouseEvents.h"
114 #include "mozilla/PresShell.h"
115 #include "mozilla/ProcessHangMonitor.h"
116 #include "mozilla/StaticPrefs_dom.h"
117 #include "mozilla/StaticPrefs_full_screen_api.h"
118 #include "mozilla/StaticPrefs_print.h"
119 #include "mozilla/StaticPrefs_fission.h"
120 #include "mozilla/ThrottledEventQueue.h"
121 #include "AudioChannelService.h"
122 #include "nsAboutProtocolUtils.h"
123 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
124 #include "PostMessageEvent.h"
125 #include "mozilla/dom/DocGroup.h"
126 #include "mozilla/net/CookieJarSettings.h"
129 #include "nsIFrame.h"
130 #include "nsCanvasFrame.h"
131 #include "nsIWidget.h"
132 #include "nsIWidgetListener.h"
133 #include "nsIBaseWindow.h"
134 #include "nsIDeviceSensors.h"
135 #include "nsIContent.h"
136 #include "nsIDocShell.h"
137 #include "mozilla/dom/Document.h"
139 #include "nsDOMString.h"
140 #include "nsThreadUtils.h"
141 #include "nsILoadContext.h"
143 #include "nsViewManager.h"
144 #include "nsIPrompt.h"
145 #include "nsIPromptService.h"
146 #include "nsIPromptFactory.h"
147 #include "nsIWritablePropertyBag2.h"
148 #include "nsIWebNavigation.h"
149 #include "nsIWebBrowserChrome.h"
150 #include "nsIWebBrowserFind.h" // For window.find()
151 #include "nsComputedDOMStyle.h"
152 #include "nsDOMCID.h"
153 #include "nsDOMWindowUtils.h"
154 #include "nsIWindowWatcher.h"
155 #include "nsPIWindowWatcher.h"
156 #include "nsIDocumentViewer.h"
157 #include "nsIScriptError.h"
158 #include "nsISHistory.h"
159 #include "nsIControllers.h"
160 #include "nsGlobalWindowCommands.h"
161 #include "nsQueryObject.h"
162 #include "nsContentUtils.h"
163 #include "nsCSSProps.h"
164 #include "nsIURIFixup.h"
165 #include "nsIURIMutator.h"
166 #include "mozilla/EventDispatcher.h"
167 #include "mozilla/EventStateManager.h"
168 #include "mozilla/ScrollContainerFrame.h"
169 #include "nsIObserverService.h"
170 #include "nsFocusManager.h"
171 #include "nsIAppWindow.h"
172 #include "nsServiceManagerUtils.h"
173 #include "mozilla/dom/CustomEvent.h"
174 #include "nsIScreenManager.h"
175 #include "nsIClassifiedChannel.h"
176 #include "nsIXULRuntime.h"
177 #include "xpcprivate.h"
180 # include "nsIPrintSettings.h"
181 # include "nsIPrintSettingsService.h"
182 # include "nsIWebBrowserPrint.h"
185 #include "nsWindowRoot.h"
186 #include "nsNetCID.h"
187 #include "nsIArray.h"
189 #include "nsIDOMXULCommandDispatcher.h"
191 #include "mozilla/GlobalKeyListener.h"
193 #include "nsIDragService.h"
194 #include "mozilla/dom/Element.h"
195 #include "mozilla/dom/Selection.h"
196 #include "nsFrameLoader.h"
197 #include "nsFrameLoaderOwner.h"
198 #include "nsXPCOMCID.h"
199 #include "mozilla/Logging.h"
200 #include "mozilla/ProfilerMarkers.h"
203 #include "mozilla/dom/IDBFactory.h"
204 #include "mozilla/dom/MessageChannel.h"
205 #include "mozilla/dom/Promise.h"
207 #include "mozilla/dom/Gamepad.h"
208 #include "mozilla/dom/GamepadManager.h"
212 #include "FxRWindowManager.h"
213 #include "mozilla/dom/VRDisplay.h"
214 #include "mozilla/dom/VRDisplayEvent.h"
215 #include "mozilla/dom/VRDisplayEventBinding.h"
216 #include "mozilla/dom/VREventObserver.h"
218 #include "nsRefreshDriver.h"
220 #include "mozilla/extensions/WebExtensionPolicy.h"
222 #include "mozilla/BasePrincipal.h"
223 #include "mozilla/Services.h"
224 #include "mozilla/Telemetry.h"
225 #include "mozilla/dom/Location.h"
226 #include "nsHTMLDocument.h"
227 #include "nsWrapperCacheInlines.h"
228 #include "mozilla/DOMEventTargetHelper.h"
230 #include "nsSandboxFlags.h"
231 #include "nsXULControllers.h"
232 #include "mozilla/dom/AudioContext.h"
233 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
234 #include "mozilla/dom/BrowsingContextGroup.h"
235 #include "mozilla/dom/cache/CacheStorage.h"
236 #include "mozilla/dom/Console.h"
237 #include "mozilla/dom/Fetch.h"
238 #include "mozilla/dom/FunctionBinding.h"
239 #include "mozilla/dom/HashChangeEvent.h"
240 #include "mozilla/dom/IntlUtils.h"
241 #include "mozilla/dom/PopStateEvent.h"
242 #include "mozilla/dom/PopupBlockedEvent.h"
243 #include "mozilla/dom/PrimitiveConversions.h"
244 #include "mozilla/dom/WindowBinding.h"
245 #include "nsIBrowserChild.h"
246 #include "mozilla/dom/MediaQueryList.h"
247 #include "mozilla/dom/NavigatorBinding.h"
248 #include "mozilla/dom/ImageBitmap.h"
249 #include "mozilla/dom/ImageBitmapBinding.h"
250 #include "mozilla/dom/ServiceWorkerRegistration.h"
251 #include "mozilla/dom/WebIDLGlobalNameHash.h"
252 #include "mozilla/dom/Worklet.h"
253 #include "AccessCheck.h"
256 # include "mozilla/dom/SpeechSynthesis.h"
260 # include <android/log.h>
264 # include <process.h>
265 # define getpid _getpid
267 # include <unistd.h> // for getpid()
270 using namespace mozilla
;
271 using namespace mozilla::dom
;
272 using namespace mozilla::dom::ipc
;
273 using mozilla::BasePrincipal
;
274 using mozilla::OriginAttributes
;
275 using mozilla::TimeStamp
;
276 using mozilla::layout::RemotePrintJobChild
;
278 static inline nsGlobalWindowInner
* GetCurrentInnerWindowInternal(
279 const nsGlobalWindowOuter
* aOuter
) {
280 return nsGlobalWindowInner::Cast(aOuter
->GetCurrentInnerWindow());
283 #define FORWARD_TO_INNER(method, args, err_rval) \
285 if (!mInnerWindow) { \
286 NS_WARNING("No inner window available!"); \
289 return GetCurrentInnerWindowInternal(this)->method args; \
292 #define FORWARD_TO_INNER_VOID(method, args) \
294 if (!mInnerWindow) { \
295 NS_WARNING("No inner window available!"); \
298 GetCurrentInnerWindowInternal(this)->method args; \
302 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an
303 // inner doesn't already exists.
304 #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \
306 if (!mInnerWindow) { \
310 nsCOMPtr<Document> kungFuDeathGrip = GetDoc(); \
311 ::mozilla::Unused << kungFuDeathGrip; \
312 if (!mInnerWindow) { \
316 return GetCurrentInnerWindowInternal(this)->method args; \
319 static LazyLogModule
gDOMLeakPRLogOuter("DOMLeakOuter");
320 extern LazyLogModule gPageCacheLog
;
323 static LazyLogModule
gDocShellAndDOMWindowLeakLogging(
324 "DocShellAndDOMWindowLeak");
327 nsGlobalWindowOuter::OuterWindowByIdTable
*
328 nsGlobalWindowOuter::sOuterWindowsById
= nullptr;
331 nsPIDOMWindowOuter
* nsPIDOMWindowOuter::GetFromCurrentInner(
332 nsPIDOMWindowInner
* aInner
) {
337 nsPIDOMWindowOuter
* outer
= aInner
->GetOuterWindow();
338 if (!outer
|| outer
->GetCurrentInnerWindow() != aInner
) {
345 //*****************************************************************************
346 // nsOuterWindowProxy: Outer Window Proxy
347 //*****************************************************************************
349 // Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so
350 // JSObject::swap can swap it with CrossCompartmentWrappers without requiring
353 // We store the nsGlobalWindowOuter* in our first slot.
355 // We store our holder weakmap in the second slot.
356 const JSClass OuterWindowProxyClass
= PROXY_CLASS_DEF(
357 "Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
359 static const size_t OUTER_WINDOW_SLOT
= 0;
360 static const size_t HOLDER_WEAKMAP_SLOT
= 1;
362 class nsOuterWindowProxy
: public MaybeCrossOriginObject
<js::Wrapper
> {
363 using Base
= MaybeCrossOriginObject
<js::Wrapper
>;
366 constexpr nsOuterWindowProxy() : Base(0) {}
368 bool finalizeInBackground(const JS::Value
& priv
) const override
{
372 // Standard internal methods
374 * Implementation of [[GetOwnProperty]] as defined at
375 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
377 * "proxy" is the WindowProxy object involved. It may not be same-compartment
380 bool getOwnPropertyDescriptor(
381 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
382 JS::MutableHandle
<Maybe
<JS::PropertyDescriptor
>> desc
) const override
;
385 * Implementation of the same-origin case of
386 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty>.
388 bool definePropertySameOrigin(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
390 JS::Handle
<JS::PropertyDescriptor
> desc
,
391 JS::ObjectOpResult
& result
) const override
;
394 * Implementation of [[OwnPropertyKeys]] as defined at
396 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys
398 * "proxy" is the WindowProxy object involved. It may not be same-compartment
401 bool ownPropertyKeys(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
402 JS::MutableHandleVector
<jsid
> props
) const override
;
404 * Implementation of [[Delete]] as defined at
405 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
407 * "proxy" is the WindowProxy object involved. It may not be same-compartment
410 bool delete_(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
411 JS::ObjectOpResult
& result
) const override
;
414 * Implementaton of hook for superclass getPrototype() method.
416 JSObject
* getSameOriginPrototype(JSContext
* cx
) const override
;
419 * Implementation of [[HasProperty]] internal method as defined at
420 * https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
422 * "proxy" is the WindowProxy object involved. It may not be same-compartment
425 * Note that the HTML spec does not define an override for this internal
426 * method, so we just want the "normal object" behavior. We have to override
427 * it, because js::Wrapper also overrides, with "not normal" behavior.
429 bool has(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
430 bool* bp
) const override
;
433 * Implementation of [[Get]] internal method as defined at
434 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get>.
436 * "proxy" is the WindowProxy object involved. It may or may not be
437 * same-compartment with "cx".
439 * "receiver" is the receiver ("this") for the get. It will be
440 * same-compartment with "cx".
442 * "vp" is the return value. It will be same-compartment with "cx".
444 bool get(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
445 JS::Handle
<JS::Value
> receiver
, JS::Handle
<jsid
> id
,
446 JS::MutableHandle
<JS::Value
> vp
) const override
;
449 * Implementation of [[Set]] internal method as defined at
450 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set>.
452 * "proxy" is the WindowProxy object involved. It may or may not be
453 * same-compartment with "cx".
455 * "v" is the value being set. It will be same-compartment with "cx".
457 * "receiver" is the receiver ("this") for the set. It will be
458 * same-compartment with "cx".
460 bool set(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
461 JS::Handle
<JS::Value
> v
, JS::Handle
<JS::Value
> receiver
,
462 JS::ObjectOpResult
& result
) const override
;
464 // SpiderMonkey extensions
466 * Implementation of SpiderMonkey extension which just checks whether this
467 * object has the property. Basically Object.getOwnPropertyDescriptor(obj,
468 * prop) !== undefined. but does not require reifying the descriptor.
470 * We have to override this because js::Wrapper overrides it, but we want
471 * different behavior from js::Wrapper.
473 * "proxy" is the WindowProxy object involved. It may not be same-compartment
476 bool hasOwn(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
477 bool* bp
) const override
;
480 * Implementation of SpiderMonkey extension which is used as a fast path for
483 * We have to override this because js::Wrapper overrides it, but we want
484 * different behavior from js::Wrapper.
486 * "proxy" is the WindowProxy object involved. It may not be same-compartment
489 bool getOwnEnumerablePropertyKeys(
490 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
491 JS::MutableHandleVector
<jsid
> props
) const override
;
494 * Hook used by SpiderMonkey to implement Object.prototype.toString.
496 const char* className(JSContext
* cx
,
497 JS::Handle
<JSObject
*> wrapper
) const override
;
499 void finalize(JS::GCContext
* gcx
, JSObject
* proxy
) const override
;
500 size_t objectMoved(JSObject
* proxy
, JSObject
* old
) const override
;
502 bool isCallable(JSObject
* obj
) const override
{ return false; }
503 bool isConstructor(JSObject
* obj
) const override
{ return false; }
505 static const nsOuterWindowProxy singleton
;
507 static nsGlobalWindowOuter
* GetOuterWindow(JSObject
* proxy
) {
508 nsGlobalWindowOuter
* outerWindow
=
509 nsGlobalWindowOuter::FromSupports(static_cast<nsISupports
*>(
510 js::GetProxyReservedSlot(proxy
, OUTER_WINDOW_SLOT
).toPrivate()));
515 // False return value means we threw an exception. True return value
516 // but false "found" means we didn't have a subframe at that index.
517 bool GetSubframeWindow(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
518 JS::Handle
<jsid
> id
, JS::MutableHandle
<JS::Value
> vp
,
521 // Returns a non-null window only if id is an index and we have a
522 // window at that index.
523 Nullable
<WindowProxyHolder
> GetSubframeWindow(JSContext
* cx
,
524 JS::Handle
<JSObject
*> proxy
,
525 JS::Handle
<jsid
> id
) const;
527 bool AppendIndexedPropertyNames(JSObject
* proxy
,
528 JS::MutableHandleVector
<jsid
> props
) const;
530 using MaybeCrossOriginObjectMixins::EnsureHolder
;
531 bool EnsureHolder(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
532 JS::MutableHandle
<JSObject
*> holder
) const override
;
534 // Helper method for creating a special "print" method that allows printing
535 // our PDF-viewer documents even if you're not same-origin with them.
537 // aProxy must be our nsOuterWindowProxy. It will not be same-compartment
538 // with aCx, since we only use this on the different-origin codepath!
540 // Can return true without filling in aDesc, which corresponds to not exposing
542 static bool MaybeGetPDFJSPrintMethod(
543 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
544 JS::MutableHandle
<Maybe
<JS::PropertyDescriptor
>> desc
);
546 // The actual "print" method we use for the PDFJS case.
547 static bool PDFJSPrintMethod(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
549 // Helper method to get the pre-PDF-viewer-messing-with-it principal from an
550 // inner window. Will return null if this is not a PDF-viewer inner or if the
551 // principal could not be found for some reason.
552 static already_AddRefed
<nsIPrincipal
> GetNoPDFJSPrincipal(
553 nsGlobalWindowInner
* inner
);
556 const char* nsOuterWindowProxy::className(JSContext
* cx
,
557 JS::Handle
<JSObject
*> proxy
) const {
558 MOZ_ASSERT(js::IsProxy(proxy
));
560 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
567 void nsOuterWindowProxy::finalize(JS::GCContext
* gcx
, JSObject
* proxy
) const {
568 nsGlobalWindowOuter
* outerWindow
= GetOuterWindow(proxy
);
570 outerWindow
->ClearWrapper(proxy
);
571 BrowsingContext
* bc
= outerWindow
->GetBrowsingContext();
573 bc
->ClearWindowProxy();
576 // Ideally we would use OnFinalize here, but it's possible that
577 // EnsureScriptEnvironment will later be called on the window, and we don't
578 // want to create a new script object in that case. Therefore, we need to
579 // write a non-null value that will reliably crash when dereferenced.
580 outerWindow
->PoisonOuterWindowProxy(proxy
);
584 bool nsOuterWindowProxy::getOwnPropertyDescriptor(
585 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
586 JS::MutableHandle
<Maybe
<JS::PropertyDescriptor
>> desc
) const {
587 // First check for indexed access. This is
588 // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
590 JS::Rooted
<JS::Value
> subframe(cx
);
592 if (!GetSubframeWindow(cx
, proxy
, id
, &subframe
, found
)) {
598 desc
.set(Some(JS::PropertyDescriptor::Data(
600 JS::PropertyAttribute::Configurable
,
601 JS::PropertyAttribute::Enumerable
,
606 bool isSameOrigin
= IsPlatformObjectSameOrigin(cx
, proxy
);
608 // If we did not find a subframe, we could still have an indexed property
609 // access. In that case we should throw a SecurityError in the cross-origin
611 if (!isSameOrigin
&& IsArrayIndex(GetArrayIndexFromId(id
))) {
613 return ReportCrossOriginDenial(cx
, id
, "access"_ns
);
616 // Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an
617 // IsArrayIndex(GetArrayIndexFromId(id)) here. We'll never have a property on
618 // the Window whose name is an index, because our defineProperty doesn't pass
619 // those on to the Window.
623 if (StaticPrefs::dom_missing_prop_counters_enabled() && id
.isAtom()) {
624 Window_Binding::CountMaybeMissingProperty(proxy
, id
);
627 // Fall through to js::Wrapper.
628 { // Scope for JSAutoRealm while we are dealing with js::Wrapper.
629 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
630 // for now. That's what js::Wrapper expects, and since we're same-origin
631 // anyway this is not changing any security behavior.
632 JSAutoRealm
ar(cx
, proxy
);
633 JS_MarkCrossZoneId(cx
, id
);
634 bool ok
= js::Wrapper::getOwnPropertyDescriptor(cx
, proxy
, id
, desc
);
640 // See https://github.com/tc39/ecma262/issues/672 for more information.
642 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx
, id
)) {
643 (*desc
).setConfigurable(true);
648 // Now wrap our descriptor back into the Realm that asked for it.
649 return JS_WrapPropertyDescriptor(cx
, desc
);
653 if (!CrossOriginGetOwnPropertyHelper(cx
, proxy
, id
, desc
)) {
662 // Non-spec step for the PDF viewer's window.print(). This comes before we
663 // check for named subframes, because in the same-origin case print() would
665 if (id
== GetJSIDByIndex(cx
, XPCJSContext::IDX_PRINT
)) {
666 if (!MaybeGetPDFJSPrintMethod(cx
, proxy
, desc
)) {
675 // Step 6 -- check for named subframes.
678 if (!name
.init(cx
, id
.toString())) {
681 nsGlobalWindowOuter
* win
= GetOuterWindow(proxy
);
682 if (RefPtr
<BrowsingContext
> childDOMWin
= win
->GetChildWindow(name
)) {
683 JS::Rooted
<JS::Value
> childValue(cx
);
684 if (!ToJSValue(cx
, WindowProxyHolder(childDOMWin
), &childValue
)) {
687 desc
.set(Some(JS::PropertyDescriptor::Data(
688 childValue
, {JS::PropertyAttribute::Configurable
})));
694 return CrossOriginPropertyFallback(cx
, proxy
, id
, desc
);
697 bool nsOuterWindowProxy::definePropertySameOrigin(
698 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
,
699 JS::Handle
<JS::PropertyDescriptor
> desc
, JS::ObjectOpResult
& result
) const {
700 if (IsArrayIndex(GetArrayIndexFromId(id
))) {
701 // Spec says to Reject whether this is a supported index or not,
702 // since we have no indexed setter or indexed creator. It is up
703 // to the caller to decide whether to throw a TypeError.
704 return result
.failCantDefineWindowElement();
707 JS::ObjectOpResult ourResult
;
708 bool ok
= js::Wrapper::defineProperty(cx
, proxy
, id
, desc
, ourResult
);
713 if (!ourResult
.ok()) {
714 // It's possible that this failed because the page got the existing
715 // descriptor (which we force to claim to be configurable) and then tried to
716 // redefine the property with the descriptor it got but a different value.
717 // We want to allow this case to succeed, so check for it and if we're in
718 // that case try again but now with an attempt to define a non-configurable
720 if (!desc
.hasConfigurable() || !desc
.configurable()) {
721 // The incoming descriptor was not explicitly marked "configurable: true",
722 // so it failed for some other reason. Just propagate that reason out.
727 JS::Rooted
<Maybe
<JS::PropertyDescriptor
>> existingDesc(cx
);
728 ok
= js::Wrapper::getOwnPropertyDescriptor(cx
, proxy
, id
, &existingDesc
);
732 if (existingDesc
.isNothing() || existingDesc
->configurable()) {
733 // We have no existing property, or its descriptor is already configurable
734 // (on the Window itself, where things really can be non-configurable).
735 // So we failed for some other reason, which we should propagate out.
740 JS::Rooted
<JS::PropertyDescriptor
> updatedDesc(cx
, desc
);
741 updatedDesc
.setConfigurable(false);
743 JS::ObjectOpResult ourNewResult
;
744 ok
= js::Wrapper::defineProperty(cx
, proxy
, id
, updatedDesc
, ourNewResult
);
749 if (!ourNewResult
.ok()) {
750 // Twiddling the configurable flag didn't help. Just return this failure
751 // out to the caller.
752 result
= ourNewResult
;
758 // See https://github.com/tc39/ecma262/issues/672 for more information.
759 if (desc
.hasConfigurable() && !desc
.configurable() &&
760 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx
, id
)) {
761 // Give callers a way to detect that they failed to "really" define a
762 // non-configurable property.
763 result
.failCantDefineWindowNonConfigurable();
772 bool nsOuterWindowProxy::ownPropertyKeys(
773 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
774 JS::MutableHandleVector
<jsid
> props
) const {
775 // Just our indexed stuff followed by our "normal" own property names.
776 if (!AppendIndexedPropertyNames(proxy
, props
)) {
780 if (IsPlatformObjectSameOrigin(cx
, proxy
)) {
781 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
782 // for now. That's what js::Wrapper expects, and since we're same-origin
783 // anyway this is not changing any security behavior.
784 JS::RootedVector
<jsid
> innerProps(cx
);
785 { // Scope for JSAutoRealm so we can mark the ids once we exit it
786 JSAutoRealm
ar(cx
, proxy
);
787 if (!js::Wrapper::ownPropertyKeys(cx
, proxy
, &innerProps
)) {
791 for (auto& id
: innerProps
) {
792 JS_MarkCrossZoneId(cx
, id
);
794 return js::AppendUnique(cx
, props
, innerProps
);
797 // In the cross-origin case we purposefully exclude subframe names from the
798 // list of property names we report here.
799 JS::Rooted
<JSObject
*> holder(cx
);
800 if (!EnsureHolder(cx
, proxy
, &holder
)) {
804 JS::RootedVector
<jsid
> crossOriginProps(cx
);
805 if (!js::GetPropertyKeys(cx
, holder
,
806 JSITER_OWNONLY
| JSITER_HIDDEN
| JSITER_SYMBOLS
,
807 &crossOriginProps
) ||
808 !js::AppendUnique(cx
, props
, crossOriginProps
)) {
812 // Add the "print" property if needed.
813 nsGlobalWindowOuter
* outer
= GetOuterWindow(proxy
);
814 nsGlobalWindowInner
* inner
=
815 nsGlobalWindowInner::Cast(outer
->GetCurrentInnerWindow());
817 nsCOMPtr
<nsIPrincipal
> targetPrincipal
= GetNoPDFJSPrincipal(inner
);
818 if (targetPrincipal
&&
819 nsContentUtils::SubjectPrincipal(cx
)->Equals(targetPrincipal
)) {
820 JS::RootedVector
<jsid
> printProp(cx
);
821 if (!printProp
.append(GetJSIDByIndex(cx
, XPCJSContext::IDX_PRINT
)) ||
822 !js::AppendUnique(cx
, props
, printProp
)) {
828 return xpc::AppendCrossOriginWhitelistedPropNames(cx
, props
);
831 bool nsOuterWindowProxy::delete_(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
833 JS::ObjectOpResult
& result
) const {
834 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
835 return ReportCrossOriginDenial(cx
, id
, "delete"_ns
);
838 if (!GetSubframeWindow(cx
, proxy
, id
).IsNull()) {
839 // Fail (which means throw if strict, else return false).
840 return result
.failCantDeleteWindowElement();
843 if (IsArrayIndex(GetArrayIndexFromId(id
))) {
844 // Indexed, but not supported. Spec says return true.
845 return result
.succeed();
848 // We're same-origin, so it should be safe to enter the Realm of "proxy".
849 // Let's do that, just in case, to avoid cross-compartment issues in our
850 // js::Wrapper caller..
851 JSAutoRealm
ar(cx
, proxy
);
852 JS_MarkCrossZoneId(cx
, id
);
853 return js::Wrapper::delete_(cx
, proxy
, id
, result
);
856 JSObject
* nsOuterWindowProxy::getSameOriginPrototype(JSContext
* cx
) const {
857 return Window_Binding::GetProtoObjectHandle(cx
);
860 bool nsOuterWindowProxy::has(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
861 JS::Handle
<jsid
> id
, bool* bp
) const {
862 // We could just directly forward this method to js::BaseProxyHandler, but
863 // that involves reifying the actual property descriptor, which might be more
864 // work than we have to do for has() on the Window.
866 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
867 // In the cross-origin case we only have own properties. Just call hasOwn
869 return hasOwn(cx
, proxy
, id
, bp
);
872 if (!GetSubframeWindow(cx
, proxy
, id
).IsNull()) {
877 // Just to be safe in terms of compartment asserts, enter the Realm of
878 // "proxy". We're same-origin with it, so this should be safe.
879 JSAutoRealm
ar(cx
, proxy
);
880 JS_MarkCrossZoneId(cx
, id
);
881 return js::Wrapper::has(cx
, proxy
, id
, bp
);
884 bool nsOuterWindowProxy::hasOwn(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
885 JS::Handle
<jsid
> id
, bool* bp
) const {
886 // We could just directly forward this method to js::BaseProxyHandler, but
887 // that involves reifying the actual property descriptor, which might be more
888 // work than we have to do for hasOwn() on the Window.
890 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
891 // Avoiding reifying the property descriptor here would require duplicating
892 // a bunch of "is this property exposed cross-origin" logic, which is
893 // probably not worth it. Just forward this along to the base
896 // It's very important to not forward this to js::Wrapper, because that will
897 // not do the right security and cross-origin checks and will pass through
898 // the call to the Window.
900 // The BaseProxyHandler code is OK with this happening without entering the
901 // compartment of "proxy".
902 return js::BaseProxyHandler::hasOwn(cx
, proxy
, id
, bp
);
905 if (!GetSubframeWindow(cx
, proxy
, id
).IsNull()) {
910 // Just to be safe in terms of compartment asserts, enter the Realm of
911 // "proxy". We're same-origin with it, so this should be safe.
912 JSAutoRealm
ar(cx
, proxy
);
913 JS_MarkCrossZoneId(cx
, id
);
914 return js::Wrapper::hasOwn(cx
, proxy
, id
, bp
);
917 bool nsOuterWindowProxy::get(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
918 JS::Handle
<JS::Value
> receiver
,
920 JS::MutableHandle
<JS::Value
> vp
) const {
921 if (id
== GetJSIDByIndex(cx
, XPCJSContext::IDX_WRAPPED_JSOBJECT
) &&
922 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx
))) {
923 vp
.set(JS::ObjectValue(*proxy
));
924 return MaybeWrapValue(cx
, vp
);
927 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
928 return CrossOriginGet(cx
, proxy
, receiver
, id
, vp
);
932 if (!GetSubframeWindow(cx
, proxy
, id
, vp
, found
)) {
940 if (StaticPrefs::dom_missing_prop_counters_enabled() && id
.isAtom()) {
941 Window_Binding::CountMaybeMissingProperty(proxy
, id
);
944 { // Scope for JSAutoRealm
945 // Enter "proxy"'s Realm. We're in the same-origin case, so this should be
947 JSAutoRealm
ar(cx
, proxy
);
949 JS_MarkCrossZoneId(cx
, id
);
951 JS::Rooted
<JS::Value
> wrappedReceiver(cx
, receiver
);
952 if (!MaybeWrapValue(cx
, &wrappedReceiver
)) {
956 // Fall through to js::Wrapper.
957 if (!js::Wrapper::get(cx
, proxy
, wrappedReceiver
, id
, vp
)) {
962 // Make sure our return value is in the caller compartment.
963 return MaybeWrapValue(cx
, vp
);
966 bool nsOuterWindowProxy::set(JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
967 JS::Handle
<jsid
> id
, JS::Handle
<JS::Value
> v
,
968 JS::Handle
<JS::Value
> receiver
,
969 JS::ObjectOpResult
& result
) const {
970 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
971 return CrossOriginSet(cx
, proxy
, id
, v
, receiver
, result
);
974 if (IsArrayIndex(GetArrayIndexFromId(id
))) {
975 // Reject the set. It's up to the caller to decide whether to throw a
976 // TypeError. If the caller is strict mode JS code, it'll throw.
977 return result
.failReadOnly();
980 // Do the rest in the Realm of "proxy", since we're in the same-origin case.
981 JSAutoRealm
ar(cx
, proxy
);
982 JS::Rooted
<JS::Value
> wrappedArg(cx
, v
);
983 if (!MaybeWrapValue(cx
, &wrappedArg
)) {
986 JS::Rooted
<JS::Value
> wrappedReceiver(cx
, receiver
);
987 if (!MaybeWrapValue(cx
, &wrappedReceiver
)) {
991 JS_MarkCrossZoneId(cx
, id
);
993 return js::Wrapper::set(cx
, proxy
, id
, wrappedArg
, wrappedReceiver
, result
);
996 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
997 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
998 JS::MutableHandleVector
<jsid
> props
) const {
999 // We could just stop overring getOwnEnumerablePropertyKeys and let our
1000 // superclasses deal (by falling back on the BaseProxyHandler implementation
1001 // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to
1002 // only return the enumerable ones. But maybe there's value in having
1003 // somewhat faster for-in iteration on Window objects...
1005 // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable
1006 // own property names.
1007 if (!AppendIndexedPropertyNames(proxy
, props
)) {
1011 if (!IsPlatformObjectSameOrigin(cx
, proxy
)) {
1012 // All the cross-origin properties other than the indexed props are
1013 // non-enumerable, so we're done here.
1017 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
1018 // for now. That's what js::Wrapper expects, and since we're same-origin
1019 // anyway this is not changing any security behavior.
1020 JS::RootedVector
<jsid
> innerProps(cx
);
1021 { // Scope for JSAutoRealm so we can mark the ids once we exit it.
1022 JSAutoRealm
ar(cx
, proxy
);
1023 if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx
, proxy
, &innerProps
)) {
1028 for (auto& id
: innerProps
) {
1029 JS_MarkCrossZoneId(cx
, id
);
1032 return js::AppendUnique(cx
, props
, innerProps
);
1035 bool nsOuterWindowProxy::GetSubframeWindow(JSContext
* cx
,
1036 JS::Handle
<JSObject
*> proxy
,
1037 JS::Handle
<jsid
> id
,
1038 JS::MutableHandle
<JS::Value
> vp
,
1039 bool& found
) const {
1040 Nullable
<WindowProxyHolder
> frame
= GetSubframeWindow(cx
, proxy
, id
);
1041 if (frame
.IsNull()) {
1047 return WrapObject(cx
, frame
.Value(), vp
);
1050 Nullable
<WindowProxyHolder
> nsOuterWindowProxy::GetSubframeWindow(
1051 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
, JS::Handle
<jsid
> id
) const {
1052 uint32_t index
= GetArrayIndexFromId(id
);
1053 if (!IsArrayIndex(index
)) {
1057 nsGlobalWindowOuter
* win
= GetOuterWindow(proxy
);
1058 return win
->IndexedGetterOuter(index
);
1061 bool nsOuterWindowProxy::AppendIndexedPropertyNames(
1062 JSObject
* proxy
, JS::MutableHandleVector
<jsid
> props
) const {
1063 uint32_t length
= GetOuterWindow(proxy
)->Length();
1064 MOZ_ASSERT(int32_t(length
) >= 0);
1065 if (!props
.reserve(props
.length() + length
)) {
1068 for (int32_t i
= 0; i
< int32_t(length
); ++i
) {
1069 if (!props
.append(JS::PropertyKey::Int(i
))) {
1077 bool nsOuterWindowProxy::EnsureHolder(
1078 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
1079 JS::MutableHandle
<JSObject
*> holder
) const {
1080 return EnsureHolder(cx
, proxy
, HOLDER_WEAKMAP_SLOT
,
1081 Window_Binding::sCrossOriginProperties
, holder
);
1084 size_t nsOuterWindowProxy::objectMoved(JSObject
* obj
, JSObject
* old
) const {
1085 nsGlobalWindowOuter
* outerWindow
= GetOuterWindow(obj
);
1087 outerWindow
->UpdateWrapper(obj
, old
);
1088 BrowsingContext
* bc
= outerWindow
->GetBrowsingContext();
1090 bc
->UpdateWindowProxy(obj
, old
);
1096 enum { PDFJS_SLOT_CALLEE
= 0 };
1099 bool nsOuterWindowProxy::MaybeGetPDFJSPrintMethod(
1100 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
,
1101 JS::MutableHandle
<Maybe
<JS::PropertyDescriptor
>> desc
) {
1103 MOZ_ASSERT(!desc
.isSome());
1105 nsGlobalWindowOuter
* outer
= GetOuterWindow(proxy
);
1106 nsGlobalWindowInner
* inner
=
1107 nsGlobalWindowInner::Cast(outer
->GetCurrentInnerWindow());
1109 // No print method to expose.
1113 nsCOMPtr
<nsIPrincipal
> targetPrincipal
= GetNoPDFJSPrincipal(inner
);
1114 if (!targetPrincipal
) {
1115 // Nothing special to be done.
1119 if (!nsContentUtils::SubjectPrincipal(cx
)->Equals(targetPrincipal
)) {
1120 // Not our origin's PDF document.
1124 // Get the function we plan to actually call.
1125 JS::Rooted
<JSObject
*> innerObj(cx
, inner
->GetGlobalJSObject());
1127 // Really should not happen, but ok, let's just return.
1131 JS::Rooted
<JS::Value
> targetFunc(cx
);
1133 JSAutoRealm
ar(cx
, innerObj
);
1134 if (!JS_GetProperty(cx
, innerObj
, "print", &targetFunc
)) {
1139 if (!targetFunc
.isObject()) {
1140 // Who knows what's going on. Just return.
1144 // The Realm of cx is the realm our caller is in and the realm we
1145 // should create our function in. Note that we can't use the
1146 // standard XPConnect function forwarder machinery because our
1147 // "this" is cross-origin, so we have to do thus by hand.
1149 // Make sure targetFunc is wrapped into the right compartment.
1150 if (!MaybeWrapValue(cx
, &targetFunc
)) {
1155 js::NewFunctionWithReserved(cx
, PDFJSPrintMethod
, 0, 0, "print");
1160 JS::Rooted
<JSObject
*> funObj(cx
, JS_GetFunctionObject(fun
));
1161 js::SetFunctionNativeReserved(funObj
, PDFJS_SLOT_CALLEE
, targetFunc
);
1163 // { value: <print>, writable: true, enumerable: true, configurable: true }
1164 // because that's what it would have been in the same-origin case without
1165 // the PDF viewer messing with things.
1166 desc
.set(Some(JS::PropertyDescriptor::Data(
1167 JS::ObjectValue(*funObj
),
1168 {JS::PropertyAttribute::Configurable
, JS::PropertyAttribute::Enumerable
,
1169 JS::PropertyAttribute::Writable
})));
1174 bool nsOuterWindowProxy::PDFJSPrintMethod(JSContext
* cx
, unsigned argc
,
1176 JS::CallArgs args
= CallArgsFromVp(argc
, vp
);
1178 JS::Rooted
<JSObject
*> realCallee(
1179 cx
, &js::GetFunctionNativeReserved(&args
.callee(), PDFJS_SLOT_CALLEE
)
1181 // Unchecked unwrap, because we want to extract the thing we really had
1183 realCallee
= js::UncheckedUnwrap(realCallee
);
1185 JS::Rooted
<JS::Value
> thisv(cx
, args
.thisv());
1186 if (thisv
.isNullOrUndefined()) {
1187 // Replace it with the global of our stashed callee, simulating the
1188 // global-assuming behavior of DOM methods.
1189 JS::Rooted
<JSObject
*> global(cx
, JS::GetNonCCWObjectGlobal(realCallee
));
1190 if (!MaybeWrapObject(cx
, &global
)) {
1193 thisv
.setObject(*global
);
1194 } else if (!thisv
.isObject()) {
1195 return ThrowInvalidThis(cx
, args
, false, prototypes::id::Window
);
1198 // We want to do an UncheckedUnwrap here, because we're going to directly
1199 // examine the principal of the inner window, if we have an inner window.
1200 JS::Rooted
<JSObject
*> unwrappedObj(cx
,
1201 js::UncheckedUnwrap(&thisv
.toObject()));
1202 nsGlobalWindowInner
* inner
= nullptr;
1204 // Do the unwrap in the Realm of the object we're looking at.
1205 JSAutoRealm
ar(cx
, unwrappedObj
);
1206 UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window
, &unwrappedObj
, inner
, cx
);
1209 return ThrowInvalidThis(cx
, args
, false, prototypes::id::Window
);
1212 nsIPrincipal
* callerPrincipal
= nsContentUtils::SubjectPrincipal(cx
);
1213 if (!callerPrincipal
->SubsumesConsideringDomain(inner
->GetPrincipal())) {
1214 // Check whether it's a PDF viewer from our origin.
1215 nsCOMPtr
<nsIPrincipal
> pdfPrincipal
= GetNoPDFJSPrincipal(inner
);
1216 if (!pdfPrincipal
|| !callerPrincipal
->Equals(pdfPrincipal
)) {
1218 return ThrowInvalidThis(cx
, args
, true, prototypes::id::Window
);
1222 // Go ahead and enter the Realm of our real callee to call it. We'll pass it
1223 // our "thisv", just in case someone grabs a "print" method off one PDF
1224 // document and .call()s it on another one.
1226 JSAutoRealm
ar(cx
, realCallee
);
1227 if (!MaybeWrapValue(cx
, &thisv
)) {
1231 // Don't bother passing through the args; they will get ignored anyway.
1233 if (!JS::Call(cx
, thisv
, realCallee
, JS::HandleValueArray::empty(),
1239 // Wrap the return value (not that there should be any!) into the right
1241 return MaybeWrapValue(cx
, args
.rval());
1245 already_AddRefed
<nsIPrincipal
> nsOuterWindowProxy::GetNoPDFJSPrincipal(
1246 nsGlobalWindowInner
* inner
) {
1247 if (!nsContentUtils::IsPDFJS(inner
->GetPrincipal())) {
1251 if (Document
* doc
= inner
->GetExtantDoc()) {
1252 if (nsCOMPtr
<nsIPropertyBag2
> propBag
=
1253 do_QueryInterface(doc
->GetChannel())) {
1254 nsCOMPtr
<nsIPrincipal
> principal(
1255 do_GetProperty(propBag
, u
"noPDFJSPrincipal"_ns
));
1256 return principal
.forget();
1262 const nsOuterWindowProxy
nsOuterWindowProxy::singleton
;
1264 class nsChromeOuterWindowProxy
: public nsOuterWindowProxy
{
1266 constexpr nsChromeOuterWindowProxy() = default;
1268 const char* className(JSContext
* cx
,
1269 JS::Handle
<JSObject
*> wrapper
) const override
;
1271 static const nsChromeOuterWindowProxy singleton
;
1274 const char* nsChromeOuterWindowProxy::className(
1275 JSContext
* cx
, JS::Handle
<JSObject
*> proxy
) const {
1276 MOZ_ASSERT(js::IsProxy(proxy
));
1278 return "ChromeWindow";
1281 const nsChromeOuterWindowProxy
nsChromeOuterWindowProxy::singleton
;
1283 static JSObject
* NewOuterWindowProxy(JSContext
* cx
,
1284 JS::Handle
<JSObject
*> global
,
1286 MOZ_ASSERT(JS_IsGlobalObject(global
));
1288 JSAutoRealm
ar(cx
, global
);
1290 js::WrapperOptions options
;
1291 options
.setClass(&OuterWindowProxyClass
);
1293 js::Wrapper::New(cx
, global
,
1294 isChrome
? &nsChromeOuterWindowProxy::singleton
1295 : &nsOuterWindowProxy::singleton
,
1297 MOZ_ASSERT_IF(obj
, js::IsWindowProxy(obj
));
1301 //*****************************************************************************
1302 //*** nsGlobalWindowOuter: Object Management
1303 //*****************************************************************************
1305 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID
)
1306 : nsPIDOMWindowOuter(aWindowID
),
1307 mFullscreenHasChangedDuringProcessing(false),
1308 mForceFullScreenInWidget(false),
1311 mHavePendingClose(false),
1312 mBlockScriptedClosingFlag(false),
1314 mCreatingInnerWindow(false),
1316 mAllowScriptsToClose(false),
1317 mTopLevelOuterContentWindow(false),
1318 mDelayedPrintUntilAfterLoad(false),
1319 mDelayedCloseForPrinting(false),
1320 mShouldDelayPrintUntilAfterLoad(false),
1323 mSetOpenerWindowCalled(false),
1326 mCanSkipCCGeneration(0),
1327 mAutoActivateVRDisplayID(0) {
1328 AssertIsOnMainThread();
1329 SetIsOnMainThread();
1330 nsLayoutStatics::AddRef();
1332 // Initialize the PRCList (this).
1333 PR_INIT_CLIST(this);
1335 // |this| is an outer window. Outer windows start out frozen and
1336 // remain frozen until they get an inner window.
1337 MOZ_ASSERT(IsFrozen());
1339 // We could have failed the first time through trying
1340 // to create the entropy collector, so we should
1341 // try to get one until we succeed.
1344 mSerial
= nsContentUtils::InnerOrOuterWindowCreated();
1346 MOZ_LOG(gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
,
1347 ("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1348 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1349 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial
,
1353 MOZ_LOG(gDOMLeakPRLogOuter
, LogLevel::Debug
,
1354 ("DOMWINDOW %p created outer=nullptr", this));
1356 // Add ourselves to the outer windows list.
1357 MOZ_ASSERT(sOuterWindowsById
, "Outer Windows hash table must be created!");
1359 // |this| is an outer window, add to the outer windows list.
1360 MOZ_ASSERT(!sOuterWindowsById
->Contains(mWindowID
),
1361 "This window shouldn't be in the hash table yet!");
1362 // We seem to see crashes in release builds because of null
1363 // |sOuterWindowsById|.
1364 if (sOuterWindowsById
) {
1365 sOuterWindowsById
->InsertOrUpdate(mWindowID
, this);
1372 void nsGlobalWindowOuter::AssertIsOnMainThread() {
1373 MOZ_ASSERT(NS_IsMainThread());
1379 void nsGlobalWindowOuter::Init() {
1380 AssertIsOnMainThread();
1382 NS_ASSERTION(gDOMLeakPRLogOuter
,
1383 "gDOMLeakPRLogOuter should have been initialized!");
1385 sOuterWindowsById
= new OuterWindowByIdTable();
1388 nsGlobalWindowOuter::~nsGlobalWindowOuter() {
1389 AssertIsOnMainThread();
1391 if (sOuterWindowsById
) {
1392 sOuterWindowsById
->Remove(mWindowID
);
1395 nsContentUtils::InnerOrOuterWindowDestroyed();
1398 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
)) {
1400 if (mLastOpenedURI
) {
1401 url
= mLastOpenedURI
->GetSpecOrDefault();
1403 // Data URLs can be very long, so truncate to avoid flooding the log.
1404 const uint32_t maxURLLength
= 1000;
1405 if (url
.Length() > maxURLLength
) {
1406 url
.Truncate(maxURLLength
);
1411 gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
,
1412 ("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1414 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1415 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial
,
1416 nullptr, url
.get()));
1420 MOZ_LOG(gDOMLeakPRLogOuter
, LogLevel::Debug
,
1421 ("DOMWINDOW %p destroyed", this));
1423 JSObject
* proxy
= GetWrapperMaybeDead();
1425 if (mBrowsingContext
&& mBrowsingContext
->GetUnbarrieredWindowProxy()) {
1426 nsGlobalWindowOuter
* outer
= nsOuterWindowProxy::GetOuterWindow(
1427 mBrowsingContext
->GetUnbarrieredWindowProxy());
1428 // Check that the current WindowProxy object corresponds to this
1429 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
1430 // we've replaced it with a cross-process WindowProxy.
1431 if (outer
== this) {
1432 mBrowsingContext
->ClearWindowProxy();
1435 js::SetProxyReservedSlot(proxy
, OUTER_WINDOW_SLOT
,
1436 JS::PrivateValue(nullptr));
1439 // An outer window is destroyed with inner windows still possibly
1440 // alive, iterate through the inner windows and null out their
1441 // back pointer to this outer, and pull them out of the list of
1444 // Our linked list of inner windows both contains (an nsGlobalWindowOuter),
1445 // and our inner windows (nsGlobalWindowInners). This means that we need to
1446 // use PRCList*. We can then compare that PRCList* to `this` to see if its an
1447 // inner or outer window.
1449 while ((w
= PR_LIST_HEAD(this)) != this) {
1450 PR_REMOVE_AND_INIT_LINK(w
);
1453 DropOuterWindowDocs();
1455 // Outer windows are always supposed to call CleanUp before letting themselves
1457 MOZ_ASSERT(mCleanedUp
);
1459 nsCOMPtr
<nsIDeviceSensors
> ac
= do_GetService(NS_DEVICE_SENSORS_CONTRACTID
);
1460 if (ac
) ac
->RemoveWindowAsListener(this);
1462 nsLayoutStatics::Release();
1466 void nsGlobalWindowOuter::ShutDown() {
1467 AssertIsOnMainThread();
1469 delete sOuterWindowsById
;
1470 sOuterWindowsById
= nullptr;
1473 void nsGlobalWindowOuter::DropOuterWindowDocs() {
1474 MOZ_ASSERT_IF(mDoc
, !mDoc
->EventHandlingSuppressed());
1476 mSuspendedDocs
.Clear();
1479 void nsGlobalWindowOuter::CleanUp() {
1480 // Guarantee idempotence.
1481 if (mCleanedUp
) return;
1486 mWindowUtils
= nullptr;
1490 mContext
= nullptr; // Forces Release
1491 mChromeEventHandler
= nullptr; // Forces Release
1492 mParentTarget
= nullptr;
1493 mMessageManager
= nullptr;
1495 mArguments
= nullptr;
1498 void nsGlobalWindowOuter::ClearControllers() {
1501 mControllers
->GetControllerCount(&count
);
1504 nsCOMPtr
<nsIController
> controller
;
1505 mControllers
->GetControllerAt(count
, getter_AddRefs(controller
));
1507 nsCOMPtr
<nsIControllerContext
> context
= do_QueryInterface(controller
);
1508 if (context
) context
->SetCommandContext(nullptr);
1511 mControllers
= nullptr;
1515 //*****************************************************************************
1516 // nsGlobalWindowOuter::nsISupports
1517 //*****************************************************************************
1519 // QueryInterface implementation for nsGlobalWindowOuter
1520 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowOuter
)
1521 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1522 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, EventTarget
)
1523 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow
)
1524 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject
)
1525 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject
)
1526 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal
)
1527 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget
)
1528 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter
)
1529 NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy
)
1530 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
1531 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
1532 NS_INTERFACE_MAP_END
1534 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter
)
1535 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter
)
1537 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter
)
1538 if (tmp
->IsBlackForCC(false)) {
1539 if (nsCCUncollectableMarker::InGeneration(tmp
->mCanSkipCCGeneration
)) {
1542 tmp
->mCanSkipCCGeneration
= nsCCUncollectableMarker::sGeneration
;
1543 if (EventListenerManager
* elm
= tmp
->GetExistingListenerManager()) {
1548 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1550 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowOuter
)
1551 return tmp
->IsBlackForCC(true);
1552 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1554 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowOuter
)
1555 return tmp
->IsBlackForCC(false);
1556 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1558 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowOuter
)
1560 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter
)
1561 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
1564 if (tmp
->mDoc
&& tmp
->mDoc
->GetDocumentURI()) {
1565 uri
= tmp
->mDoc
->GetDocumentURI()->GetSpecOrDefault();
1567 SprintfLiteral(name
, "nsGlobalWindowOuter # %" PRIu64
" outer %s",
1568 tmp
->mWindowID
, uri
.get());
1569 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
1571 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowOuter
, tmp
->mRefCnt
.get())
1574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext
)
1576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers
)
1577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments
)
1579 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage
)
1580 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDocs
)
1581 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal
)
1582 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentCookiePrincipal
)
1583 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal
)
1584 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPartitionedPrincipal
)
1585 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc
)
1587 // Traverse stuff from nsPIDOMWindow
1588 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler
)
1589 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget
)
1590 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager
)
1591 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement
)
1593 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell
)
1594 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext
)
1596 tmp
->TraverseObjectsInGlobal(cb
);
1598 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields
.mBrowserDOMWindow
)
1599 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1601 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter
)
1602 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
1603 if (sOuterWindowsById
) {
1604 sOuterWindowsById
->Remove(tmp
->mWindowID
);
1607 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext
)
1609 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers
)
1610 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments
)
1612 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage
)
1613 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDocs
)
1614 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal
)
1615 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentCookiePrincipal
)
1616 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal
)
1617 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPartitionedPrincipal
)
1618 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc
)
1620 // Unlink stuff from nsPIDOMWindow
1621 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler
)
1622 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget
)
1623 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager
)
1624 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement
)
1626 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell
)
1627 if (tmp
->mBrowsingContext
) {
1628 if (tmp
->mBrowsingContext
->GetUnbarrieredWindowProxy()) {
1629 nsGlobalWindowOuter
* outer
= nsOuterWindowProxy::GetOuterWindow(
1630 tmp
->mBrowsingContext
->GetUnbarrieredWindowProxy());
1631 // Check that the current WindowProxy object corresponds to this
1632 // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if
1633 // we've replaced it with a cross-process WindowProxy.
1635 tmp
->mBrowsingContext
->ClearWindowProxy();
1638 tmp
->mBrowsingContext
= nullptr;
1641 tmp
->UnlinkObjectsInGlobal();
1643 if (tmp
->IsChromeWindow()) {
1644 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields
.mBrowserDOMWindow
)
1647 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1648 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1650 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowOuter
)
1651 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1652 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1654 bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded
) {
1655 if (!nsCCUncollectableMarker::sGeneration
) {
1659 // Unlike most wrappers, the outer window wrapper is not a wrapper for
1660 // the outer window. Instead, the outer window wrapper holds the inner
1661 // window binding object, which in turn holds the nsGlobalWindowInner, which
1662 // has a strong reference to the nsGlobalWindowOuter. We're using the
1663 // mInnerWindow pointer as a flag for that whole chain.
1664 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1665 (mInnerWindow
&& HasKnownLiveWrapper())) &&
1666 (!aTracingNeeded
|| HasNothingToTrace(ToSupports(this)));
1669 //*****************************************************************************
1670 // nsGlobalWindowOuter::nsIScriptGlobalObject
1671 //*****************************************************************************
1673 bool nsGlobalWindowOuter::ShouldResistFingerprinting(RFPTarget aTarget
) const {
1675 return mDoc
->ShouldResistFingerprinting(aTarget
);
1677 return nsContentUtils::ShouldResistFingerprinting(
1678 "If we do not have a document then we do not have any context"
1679 "to make an informed RFP choice, so we fall back to the global pref",
1683 OriginTrials
nsGlobalWindowOuter::Trials() const {
1684 return mInnerWindow
? nsGlobalWindowInner::Cast(mInnerWindow
)->Trials()
1688 FontFaceSet
* nsGlobalWindowOuter::GetFonts() {
1690 return mDoc
->Fonts();
1695 nsresult
nsGlobalWindowOuter::EnsureScriptEnvironment() {
1696 if (GetWrapperPreserveColor()) {
1700 NS_ENSURE_STATE(!mCleanedUp
);
1702 NS_ASSERTION(!GetCurrentInnerWindowInternal(this),
1703 "No cached wrapper, but we have an inner window?");
1704 NS_ASSERTION(!mContext
, "Will overwrite mContext!");
1706 // If this window is an [i]frame, don't bother GC'ing when the frame's context
1707 // is destroyed since a GC will happen when the frameset or host document is
1708 // destroyed anyway.
1709 mContext
= new nsJSContext(mBrowsingContext
->IsTop(), this);
1713 nsIScriptContext
* nsGlobalWindowOuter::GetScriptContext() { return mContext
; }
1715 bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document
* aNewDocument
) {
1716 // We reuse the inner window when:
1717 // a. We are currently at our original document.
1718 // b. At least one of the following conditions are true:
1719 // -- The new document is the same as the old document. This means that we're
1720 // getting called from document.open().
1721 // -- The new document has the same origin as what we have loaded right now.
1723 if (!mDoc
|| !aNewDocument
) {
1727 if (!mDoc
->IsInitialDocument()) {
1733 nsCOMPtr
<nsIURI
> uri
;
1734 NS_GetURIWithoutRef(mDoc
->GetDocumentURI(), getter_AddRefs(uri
));
1735 NS_ASSERTION(NS_IsAboutBlank(uri
), "How'd this happen?");
1739 // Great, we're the original document, check for one of the other
1742 if (mDoc
== aNewDocument
) {
1746 if (aNewDocument
->IsStaticDocument()) {
1750 if (BasePrincipal::Cast(mDoc
->NodePrincipal())
1751 ->FastEqualsConsideringDomain(aNewDocument
->NodePrincipal())) {
1752 // The origin is the same.
1759 void nsGlobalWindowOuter::SetInitialPrincipal(
1760 nsIPrincipal
* aNewWindowPrincipal
, nsIContentSecurityPolicy
* aCSP
,
1761 const Maybe
<nsILoadInfo::CrossOriginEmbedderPolicy
>& aCOEP
) {
1762 // We should never create windows with an expanded principal.
1763 // If we have a system principal, make sure we're not using it for a content
1765 // NOTE: Please keep this logic in sync with
1766 // nsAppShellService::JustCreateTopWindow
1767 if (nsContentUtils::IsExpandedPrincipal(aNewWindowPrincipal
) ||
1768 (aNewWindowPrincipal
->IsSystemPrincipal() &&
1769 GetBrowsingContext()->IsContent())) {
1770 aNewWindowPrincipal
= nullptr;
1773 // If there's an existing document, bail if it either:
1775 // (a) is not an initial about:blank document, or
1776 if (!mDoc
->IsInitialDocument()) return;
1777 // (b) already has the correct principal.
1778 if (mDoc
->NodePrincipal() == aNewWindowPrincipal
) return;
1781 // If we have a document loaded at this point, it had better be about:blank.
1782 // Otherwise, something is really weird. An about:blank page has a
1784 bool isNullPrincipal
;
1785 MOZ_ASSERT(NS_SUCCEEDED(mDoc
->NodePrincipal()->GetIsNullPrincipal(
1786 &isNullPrincipal
)) &&
1791 // Use the subject (or system) principal as the storage principal too until
1792 // the new window finishes navigating and gets a real storage principal.
1793 nsDocShell::Cast(GetDocShell())
1794 ->CreateAboutBlankDocumentViewer(aNewWindowPrincipal
, aNewWindowPrincipal
,
1796 /* aIsInitialDocument */ true, aCOEP
);
1799 MOZ_ASSERT(mDoc
->IsInitialDocument(),
1800 "document should be initial document");
1803 RefPtr
<PresShell
> presShell
= GetDocShell()->GetPresShell();
1804 if (presShell
&& !presShell
->DidInitialize()) {
1805 // Ensure that if someone plays with this document they will get
1806 // layout happening.
1807 presShell
->Initialize();
1811 #define WINDOWSTATEHOLDER_IID \
1812 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
1814 class WindowStateHolder final
: public nsISupports
{
1816 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID
)
1819 explicit WindowStateHolder(nsGlobalWindowInner
* aWindow
);
1821 nsGlobalWindowInner
* GetInnerWindow() { return mInnerWindow
; }
1823 void DidRestoreWindow() {
1824 mInnerWindow
= nullptr;
1825 mInnerWindowReflector
= nullptr;
1829 ~WindowStateHolder();
1831 nsGlobalWindowInner
* mInnerWindow
;
1832 // We hold onto this to make sure the inner window doesn't go away. The outer
1833 // window ends up recalculating it anyway.
1834 JS::PersistentRooted
<JSObject
*> mInnerWindowReflector
;
1837 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder
, WINDOWSTATEHOLDER_IID
)
1839 WindowStateHolder::WindowStateHolder(nsGlobalWindowInner
* aWindow
)
1840 : mInnerWindow(aWindow
),
1841 mInnerWindowReflector(RootingCx(), aWindow
->GetWrapper()) {
1842 MOZ_ASSERT(aWindow
, "null window");
1846 // When a global goes into the bfcache, we disable script.
1847 xpc::Scriptability::Get(mInnerWindowReflector
).SetWindowAllowsScript(false);
1850 WindowStateHolder::~WindowStateHolder() {
1852 // This window was left in the bfcache and is now going away. We need to
1854 // Note that FreeInnerObjects may already have been called on the
1855 // inner window if its outer has already had SetDocShell(null)
1857 mInnerWindow
->FreeInnerObjects();
1861 NS_IMPL_ISUPPORTS(WindowStateHolder
, WindowStateHolder
)
1863 bool nsGlobalWindowOuter::ComputeIsSecureContext(Document
* aDocument
,
1864 SecureContextFlags aFlags
) {
1865 nsCOMPtr
<nsIPrincipal
> principal
= aDocument
->NodePrincipal();
1866 if (principal
->IsSystemPrincipal()) {
1870 // Implement https://w3c.github.io/webappsec-secure-contexts/#settings-object
1871 // With some modifications to allow for aFlags.
1873 bool hadNonSecureContextCreator
= false;
1875 if (WindowContext
* parentWindow
=
1876 GetBrowsingContext()->GetParentWindowContext()) {
1877 hadNonSecureContextCreator
= !parentWindow
->GetIsSecureContext();
1880 if (hadNonSecureContextCreator
) {
1884 if (nsContentUtils::HttpsStateIsModern(aDocument
)) {
1888 if (principal
->GetIsNullPrincipal()) {
1889 // If the NullPrincipal has a valid precursor URI we want to use it to
1890 // construct the principal otherwise we fall back to the original document
1892 nsCOMPtr
<nsIPrincipal
> precursorPrin
= principal
->GetPrecursorPrincipal();
1893 nsCOMPtr
<nsIURI
> uri
= precursorPrin
? precursorPrin
->GetURI() : nullptr;
1895 uri
= aDocument
->GetOriginalURI();
1897 // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so
1898 // it doesn't actually matter what we use here, but reusing the document
1899 // principal's attributes is convenient.
1900 const OriginAttributes
& attrs
= principal
->OriginAttributesRef();
1901 // CreateContentPrincipal correctly gets a useful principal for blob: and
1902 // other URI_INHERITS_SECURITY_CONTEXT URIs.
1903 principal
= BasePrincipal::CreateContentPrincipal(uri
, attrs
);
1904 if (NS_WARN_IF(!principal
)) {
1909 return principal
->GetIsOriginPotentiallyTrustworthy();
1912 static bool InitializeLegacyNetscapeObject(JSContext
* aCx
,
1913 JS::Handle
<JSObject
*> aGlobal
) {
1914 JSAutoRealm
ar(aCx
, aGlobal
);
1916 // Note: MathJax depends on window.netscape being exposed. See bug 791526.
1917 JS::Rooted
<JSObject
*> obj(aCx
);
1918 obj
= JS_DefineObject(aCx
, aGlobal
, "netscape", nullptr);
1919 NS_ENSURE_TRUE(obj
, false);
1921 obj
= JS_DefineObject(aCx
, obj
, "security", nullptr);
1922 NS_ENSURE_TRUE(obj
, false);
1927 struct MOZ_STACK_CLASS CompartmentFinderState
{
1928 explicit CompartmentFinderState(nsIPrincipal
* aPrincipal
)
1929 : principal(aPrincipal
), compartment(nullptr) {}
1931 // Input: we look for a compartment which is same-origin with the
1933 nsIPrincipal
* principal
;
1935 // Output: We set this member if we find a compartment.
1936 JS::Compartment
* compartment
;
1939 static JS::CompartmentIterResult
FindSameOriginCompartment(
1940 JSContext
* aCx
, void* aData
, JS::Compartment
* aCompartment
) {
1941 auto* data
= static_cast<CompartmentFinderState
*>(aData
);
1942 MOZ_ASSERT(!data
->compartment
, "Why are we getting called?");
1944 // If this compartment is not safe to share across globals, don't do
1945 // anything with it; in particular we should not be getting a
1946 // CompartmentPrivate from such a compartment, because it may be in
1947 // the middle of being collected and its CompartmentPrivate may no
1949 if (!js::IsSharableCompartment(aCompartment
)) {
1950 return JS::CompartmentIterResult::KeepGoing
;
1953 auto* compartmentPrivate
= xpc::CompartmentPrivate::Get(aCompartment
);
1954 if (!compartmentPrivate
->CanShareCompartmentWith(data
->principal
)) {
1955 // Can't reuse this one, keep going.
1956 return JS::CompartmentIterResult::KeepGoing
;
1959 // We have a winner!
1960 data
->compartment
= aCompartment
;
1961 return JS::CompartmentIterResult::Stop
;
1964 static JS::RealmCreationOptions
& SelectZone(
1965 JSContext
* aCx
, nsIPrincipal
* aPrincipal
, nsGlobalWindowInner
* aNewInner
,
1966 JS::RealmCreationOptions
& aOptions
) {
1967 // Use the shared system compartment for chrome windows.
1968 if (aPrincipal
->IsSystemPrincipal()) {
1969 return aOptions
.setExistingCompartment(xpc::PrivilegedJunkScope());
1972 BrowsingContext
* bc
= aNewInner
->GetBrowsingContext();
1974 // We're a toplevel load. Use a new zone. This way, when we do
1975 // zone-based compartment sharing we won't share compartments
1976 // across navigations.
1977 return aOptions
.setNewCompartmentAndZone();
1980 // Find the in-process ancestor highest in the hierarchy.
1981 nsGlobalWindowInner
* ancestor
= nullptr;
1982 for (WindowContext
* wc
= bc
->GetParentWindowContext(); wc
;
1983 wc
= wc
->GetParentWindowContext()) {
1984 if (nsGlobalWindowInner
* win
= wc
->GetInnerWindow()) {
1989 // If we have an ancestor window, use its zone.
1990 if (ancestor
&& ancestor
->GetGlobalJSObject()) {
1991 JS::Zone
* zone
= JS::GetObjectZone(ancestor
->GetGlobalJSObject());
1992 // Now try to find an existing compartment that's same-origin
1993 // with our principal.
1994 CompartmentFinderState
data(aPrincipal
);
1995 JS_IterateCompartmentsInZone(aCx
, zone
, &data
, FindSameOriginCompartment
);
1996 if (data
.compartment
) {
1997 return aOptions
.setExistingCompartment(data
.compartment
);
1999 return aOptions
.setNewCompartmentInExistingZone(
2000 ancestor
->GetGlobalJSObject());
2003 return aOptions
.setNewCompartmentAndZone();
2007 * Create a new global object that will be used for an inner window.
2008 * Return the native global and an nsISupports 'holder' that can be used
2009 * to manage the lifetime of it.
2011 static nsresult
CreateNativeGlobalForInner(
2012 JSContext
* aCx
, nsGlobalWindowInner
* aNewInner
, Document
* aDocument
,
2013 JS::MutableHandle
<JSObject
*> aGlobal
, bool aIsSecureContext
,
2014 bool aDefineSharedArrayBufferConstructor
) {
2016 MOZ_ASSERT(aNewInner
);
2018 nsCOMPtr
<nsIURI
> uri
= aDocument
->GetDocumentURI();
2019 nsCOMPtr
<nsIPrincipal
> principal
= aDocument
->NodePrincipal();
2020 MOZ_ASSERT(principal
);
2022 // DOMWindow with nsEP is not supported, we have to make sure
2023 // no one creates one accidentally.
2024 nsCOMPtr
<nsIExpandedPrincipal
> nsEP
= do_QueryInterface(principal
);
2025 MOZ_RELEASE_ASSERT(!nsEP
, "DOMWindow with nsEP is not supported");
2027 JS::RealmOptions options
;
2028 JS::RealmCreationOptions
& creationOptions
= options
.creationOptions();
2030 SelectZone(aCx
, principal
, aNewInner
, creationOptions
);
2032 // Define the SharedArrayBuffer global constructor property only if shared
2033 // memory may be used and structured-cloned (e.g. through postMessage).
2035 // When the global constructor property isn't defined, the SharedArrayBuffer
2036 // constructor can still be reached through Web Assembly. Omitting the global
2037 // property just prevents feature-tests from being misled. See bug 1624266.
2038 creationOptions
.setDefineSharedArrayBufferConstructor(
2039 aDefineSharedArrayBufferConstructor
);
2041 xpc::InitGlobalObjectOptions(
2042 options
, principal
->IsSystemPrincipal(), aIsSecureContext
,
2043 aDocument
->ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC
),
2044 aDocument
->ShouldResistFingerprinting(RFPTarget::JSMathFdlibm
),
2045 aDocument
->ShouldResistFingerprinting(RFPTarget::JSLocale
));
2047 // Determine if we need the Components object.
2048 bool needComponents
= principal
->IsSystemPrincipal();
2049 uint32_t flags
= needComponents
? 0 : xpc::OMIT_COMPONENTS_OBJECT
;
2050 flags
|= xpc::DONT_FIRE_ONNEWGLOBALHOOK
;
2052 if (!Window_Binding::Wrap(aCx
, aNewInner
, aNewInner
, options
,
2053 nsJSPrincipals::get(principal
), aGlobal
) ||
2054 !xpc::InitGlobalObject(aCx
, aGlobal
, flags
)) {
2055 return NS_ERROR_FAILURE
;
2058 MOZ_ASSERT(aNewInner
->GetWrapperPreserveColor() == aGlobal
);
2060 // Set the location information for the new global, so that tools like
2061 // about:memory may use that information
2062 xpc::SetLocationForGlobal(aGlobal
, uri
);
2064 if (!InitializeLegacyNetscapeObject(aCx
, aGlobal
)) {
2065 return NS_ERROR_FAILURE
;
2071 nsresult
nsGlobalWindowOuter::SetNewDocument(Document
* aDocument
,
2072 nsISupports
* aState
,
2073 bool aForceReuseInnerWindow
,
2074 WindowGlobalChild
* aActor
) {
2075 MOZ_ASSERT(mDocumentPrincipal
== nullptr,
2076 "mDocumentPrincipal prematurely set!");
2077 MOZ_ASSERT(mDocumentCookiePrincipal
== nullptr,
2078 "mDocumentCookiePrincipal prematurely set!");
2079 MOZ_ASSERT(mDocumentStoragePrincipal
== nullptr,
2080 "mDocumentStoragePrincipal prematurely set!");
2081 MOZ_ASSERT(mDocumentPartitionedPrincipal
== nullptr,
2082 "mDocumentPartitionedPrincipal prematurely set!");
2083 MOZ_ASSERT(aDocument
);
2085 // Bail out early if we're in process of closing down the window.
2086 NS_ENSURE_STATE(!mCleanedUp
);
2088 NS_ASSERTION(!GetCurrentInnerWindow() ||
2089 GetCurrentInnerWindow()->GetExtantDoc() == mDoc
,
2090 "Uh, mDoc doesn't match the current inner window "
2092 bool wouldReuseInnerWindow
= WouldReuseInnerWindow(aDocument
);
2093 if (aForceReuseInnerWindow
&& !wouldReuseInnerWindow
&& mDoc
&&
2094 mDoc
->NodePrincipal() != aDocument
->NodePrincipal()) {
2095 NS_ERROR("Attempted forced inner window reuse while changing principal");
2096 return NS_ERROR_UNEXPECTED
;
2099 if (!mBrowsingContext
->AncestorsAreCurrent()) {
2100 return NS_ERROR_NOT_AVAILABLE
;
2103 RefPtr
<Document
> oldDoc
= mDoc
;
2104 MOZ_RELEASE_ASSERT(oldDoc
!= aDocument
);
2108 JSContext
* cx
= jsapi
.cx();
2110 // Check if we're anywhere near the stack limit before we reach the
2111 // transplanting code, since it has no good way to handle errors. This uses
2112 // the untrusted script limit, which is not strictly necessary since no
2113 // actual script should run.
2114 js::AutoCheckRecursionLimit
recursion(cx
);
2115 if (!recursion
.checkConservativeDontReport(cx
)) {
2116 NS_WARNING("Overrecursion in SetNewDocument");
2117 return NS_ERROR_FAILURE
;
2121 // First document load.
2123 // Get our private root. If it is equal to us, then we need to
2124 // attach our global key bindings that handles browser scrolling
2125 // and other browser commands.
2126 nsPIDOMWindowOuter
* privateRoot
= GetPrivateRoot();
2128 if (privateRoot
== this) {
2129 RootWindowGlobalKeyListener::AttachKeyHandler(mChromeEventHandler
);
2133 MaybeResetWindowName(aDocument
);
2135 /* No mDocShell means we're already been partially closed down. When that
2136 happens, setting status isn't a big requirement, so don't. (Doesn't happen
2137 under normal circumstances, but bug 49615 describes a case.) */
2139 nsContentUtils::AddScriptRunner(
2140 NewRunnableMethod("nsGlobalWindowOuter::ClearStatus", this,
2141 &nsGlobalWindowOuter::ClearStatus
));
2143 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
2144 // window (see bug 776497). Be safe.
2145 bool reUseInnerWindow
= (aForceReuseInnerWindow
|| wouldReuseInnerWindow
) &&
2146 GetCurrentInnerWindowInternal(this);
2150 // We set mDoc even though this is an outer window to avoid
2151 // having to *always* reach into the inner window to find the
2155 nsDocShell::Cast(mDocShell
)->MaybeRestoreWindowName();
2157 // We drop the print request for the old document on the floor, it never made
2158 // it. We don't close the window here either even if we were asked to.
2159 mShouldDelayPrintUntilAfterLoad
= true;
2160 mDelayedCloseForPrinting
= false;
2161 mDelayedPrintUntilAfterLoad
= false;
2163 // Take this opportunity to clear mSuspendedDocs. Our old inner window is now
2164 // responsible for unsuspending it.
2165 mSuspendedDocs
.Clear();
2168 mLastOpenedURI
= aDocument
->GetDocumentURI();
2171 RefPtr
<nsGlobalWindowInner
> currentInner
=
2172 GetCurrentInnerWindowInternal(this);
2174 if (currentInner
&& currentInner
->mNavigator
) {
2175 currentInner
->mNavigator
->OnNavigation();
2178 RefPtr
<nsGlobalWindowInner
> newInnerWindow
;
2179 bool createdInnerWindow
= false;
2181 bool thisChrome
= IsChromeWindow();
2183 nsCOMPtr
<WindowStateHolder
> wsh
= do_QueryInterface(aState
);
2184 NS_ASSERTION(!aState
|| wsh
,
2185 "What kind of weird state are you giving me here?");
2187 bool doomCurrentInner
= false;
2189 // Only non-gray (i.e. exposed to JS) objects should be assigned to
2191 JS::Rooted
<JSObject
*> newInnerGlobal(cx
);
2192 if (reUseInnerWindow
) {
2193 // We're reusing the current inner window.
2194 NS_ASSERTION(!currentInner
->IsFrozen(),
2195 "We should never be reusing a shared inner window");
2196 newInnerWindow
= currentInner
;
2197 newInnerGlobal
= currentInner
->GetWrapper();
2199 // We're reusing the inner window, but this still counts as a navigation,
2200 // so all expandos and such defined on the outer window should go away.
2201 // Force all Xray wrappers to be recomputed.
2202 JS::Rooted
<JSObject
*> rootedObject(cx
, GetWrapper());
2203 if (!JS_RefreshCrossCompartmentWrappers(cx
, rootedObject
)) {
2204 return NS_ERROR_FAILURE
;
2207 // Inner windows are only reused for same-origin principals, but the
2208 // principals don't necessarily match exactly. Update the principal on the
2209 // realm to match the new document. NB: We don't just call
2210 // currentInner->RefreshRealmPrincipals() here because we haven't yet set
2211 // its mDoc to aDocument.
2212 JS::Realm
* realm
= js::GetNonCCWObjectRealm(newInnerGlobal
);
2214 bool sameOrigin
= false;
2215 nsIPrincipal
* existing
= nsJSPrincipals::get(JS::GetRealmPrincipals(realm
));
2216 aDocument
->NodePrincipal()->Equals(existing
, &sameOrigin
);
2217 MOZ_ASSERT(sameOrigin
);
2219 JS::SetRealmPrincipals(realm
,
2220 nsJSPrincipals::get(aDocument
->NodePrincipal()));
2223 newInnerWindow
= wsh
->GetInnerWindow();
2224 newInnerGlobal
= newInnerWindow
->GetWrapper();
2226 newInnerWindow
= nsGlobalWindowInner::Create(this, thisChrome
, aActor
);
2227 if (StaticPrefs::dom_timeout_defer_during_load()) {
2228 // ensure the initial loading state is known
2229 newInnerWindow
->SetActiveLoadingState(
2230 aDocument
->GetReadyStateEnum() ==
2231 Document::ReadyState::READYSTATE_LOADING
);
2234 // The outer window is automatically treated as frozen when we
2235 // null out the inner window. As a result, initializing classes
2236 // on the new inner won't end up reaching into the old inner
2237 // window for classes etc.
2239 // [This happens with Object.prototype when XPConnect creates
2240 // a temporary global while initializing classes; the reason
2241 // being that xpconnect creates the temp global w/o a parent
2242 // and proto, which makes the JS engine look up classes in
2243 // cx->globalObject, i.e. this outer window].
2245 mInnerWindow
= nullptr;
2247 mCreatingInnerWindow
= true;
2249 // The SharedArrayBuffer global constructor property should not be present
2250 // in a fresh global object when shared memory objects aren't allowed
2251 // (because COOP/COEP support isn't enabled, or because COOP/COEP don't
2252 // act to isolate this page to a separate process).
2254 // Every script context we are initialized with must create a
2256 rv
= CreateNativeGlobalForInner(
2257 cx
, newInnerWindow
, aDocument
, &newInnerGlobal
,
2258 ComputeIsSecureContext(aDocument
),
2259 newInnerWindow
->IsSharedMemoryAllowedInternal(
2260 aDocument
->NodePrincipal()));
2262 NS_SUCCEEDED(rv
) && newInnerGlobal
&&
2263 newInnerWindow
->GetWrapperPreserveColor() == newInnerGlobal
,
2264 "Failed to get script global");
2266 mCreatingInnerWindow
= false;
2267 createdInnerWindow
= true;
2269 NS_ENSURE_SUCCESS(rv
, rv
);
2272 if (currentInner
&& currentInner
->GetWrapperPreserveColor()) {
2273 // Don't free objects on our current inner window if it's going to be
2274 // held in the bfcache.
2275 if (!currentInner
->IsFrozen()) {
2276 doomCurrentInner
= true;
2280 mInnerWindow
= newInnerWindow
;
2281 MOZ_ASSERT(mInnerWindow
);
2282 mInnerWindow
->TryToCacheTopInnerWindow();
2284 if (!GetWrapperPreserveColor()) {
2285 JS::Rooted
<JSObject
*> outer(
2286 cx
, NewOuterWindowProxy(cx
, newInnerGlobal
, thisChrome
));
2287 NS_ENSURE_TRUE(outer
, NS_ERROR_FAILURE
);
2289 mBrowsingContext
->CleanUpDanglingRemoteOuterWindowProxies(cx
, &outer
);
2290 MOZ_ASSERT(js::IsWindowProxy(outer
));
2292 js::SetProxyReservedSlot(outer
, OUTER_WINDOW_SLOT
,
2293 JS::PrivateValue(ToSupports(this)));
2295 // Inform the nsJSContext, which is the canonical holder of the outer.
2296 mContext
->SetWindowProxy(outer
);
2298 SetWrapper(mContext
->GetWindowProxy());
2300 JS::Rooted
<JSObject
*> outerObject(
2301 cx
, NewOuterWindowProxy(cx
, newInnerGlobal
, thisChrome
));
2303 NS_ERROR("out of memory");
2304 return NS_ERROR_FAILURE
;
2307 JS::Rooted
<JSObject
*> obj(cx
, GetWrapper());
2309 MOZ_ASSERT(js::IsWindowProxy(obj
));
2311 js::SetProxyReservedSlot(obj
, OUTER_WINDOW_SLOT
,
2312 JS::PrivateValue(nullptr));
2313 js::SetProxyReservedSlot(outerObject
, OUTER_WINDOW_SLOT
,
2314 JS::PrivateValue(nullptr));
2315 js::SetProxyReservedSlot(obj
, HOLDER_WEAKMAP_SLOT
, JS::UndefinedValue());
2317 outerObject
= xpc::TransplantObjectNukingXrayWaiver(cx
, obj
, outerObject
);
2320 mBrowsingContext
->ClearWindowProxy();
2321 NS_ERROR("unable to transplant wrappers, probably OOM");
2322 return NS_ERROR_FAILURE
;
2325 js::SetProxyReservedSlot(outerObject
, OUTER_WINDOW_SLOT
,
2326 JS::PrivateValue(ToSupports(this)));
2328 SetWrapper(outerObject
);
2330 MOZ_ASSERT(JS::GetNonCCWObjectGlobal(outerObject
) == newInnerGlobal
);
2332 // Inform the nsJSContext, which is the canonical holder of the outer.
2333 mContext
->SetWindowProxy(outerObject
);
2336 // Enter the new global's realm.
2337 JSAutoRealm
ar(cx
, GetWrapperPreserveColor());
2340 JS::Rooted
<JSObject
*> outer(cx
, GetWrapperPreserveColor());
2341 js::SetWindowProxy(cx
, newInnerGlobal
, outer
);
2342 mBrowsingContext
->SetWindowProxy(outer
);
2345 // Set scriptability based on the state of the WindowContext.
2346 WindowContext
* wc
= mInnerWindow
->GetWindowContext();
2348 wc
? wc
->CanExecuteScripts() : mBrowsingContext
->CanExecuteScripts();
2349 xpc::Scriptability::Get(GetWrapperPreserveColor())
2350 .SetWindowAllowsScript(allow
);
2353 // Get the "window" property once so it will be cached on our inner. We
2354 // have to do this here, not in binding code, because this has to happen
2355 // after we've created the outer window proxy and stashed it in the outer
2356 // nsGlobalWindowOuter, so GetWrapperPreserveColor() on that outer
2357 // nsGlobalWindowOuter doesn't return null and
2358 // nsGlobalWindowOuter::OuterObject works correctly.
2359 JS::Rooted
<JS::Value
> unused(cx
);
2360 if (!JS_GetProperty(cx
, newInnerGlobal
, "window", &unused
)) {
2361 NS_ERROR("can't create the 'window' property");
2362 return NS_ERROR_FAILURE
;
2365 // And same thing for the "self" property.
2366 if (!JS_GetProperty(cx
, newInnerGlobal
, "self", &unused
)) {
2367 NS_ERROR("can't create the 'self' property");
2368 return NS_ERROR_FAILURE
;
2373 JSAutoRealm
ar(cx
, GetWrapperPreserveColor());
2375 if (!aState
&& !reUseInnerWindow
) {
2376 // Loading a new page and creating a new inner window, *not*
2377 // restoring from session history.
2379 // Now that both the the inner and outer windows are initialized
2380 // let the script context do its magic to hook them together.
2381 MOZ_ASSERT(mContext
->GetWindowProxy() == GetWrapperPreserveColor());
2383 JS::Rooted
<JSObject
*> rootedJSObject(cx
, GetWrapperPreserveColor());
2384 JS::Rooted
<JSObject
*> proto1(cx
), proto2(cx
);
2385 JS_GetPrototype(cx
, rootedJSObject
, &proto1
);
2386 JS_GetPrototype(cx
, newInnerGlobal
, &proto2
);
2387 NS_ASSERTION(proto1
== proto2
,
2388 "outer and inner globals should have the same prototype");
2391 mInnerWindow
->SyncStateFromParentWindow();
2394 // Add an extra ref in case we release mContext during GC.
2395 nsCOMPtr
<nsIScriptContext
> kungFuDeathGrip(mContext
);
2397 // Make sure the inner's document is set correctly before we call
2398 // SetScriptGlobalObject, because that might try to examine document-dependent
2399 // state. Unfortunately, we can't do some of the other clearing/resetting
2400 // work we do below until after SetScriptGlobalObject(), because it might
2401 // depend on the document having the right scope object.
2403 MOZ_RELEASE_ASSERT(newInnerWindow
->mDoc
== aDocument
);
2405 if (reUseInnerWindow
) {
2406 MOZ_RELEASE_ASSERT(newInnerWindow
->mDoc
!= aDocument
);
2408 newInnerWindow
->mDoc
= aDocument
;
2411 aDocument
->SetScriptGlobalObject(newInnerWindow
);
2413 MOZ_RELEASE_ASSERT(newInnerWindow
->mDoc
== aDocument
);
2415 if (mBrowsingContext
->IsTopContent()) {
2416 net::CookieJarSettings::Cast(aDocument
->CookieJarSettings())
2417 ->SetTopLevelWindowContextId(aDocument
->InnerWindowID());
2420 newInnerWindow
->RefreshReduceTimerPrecisionCallerType();
2423 if (reUseInnerWindow
) {
2424 // The StorageAccess state may have changed. Invalidate the cached
2425 // StorageAllowed field, so that the next call to StorageAllowedForWindow
2427 newInnerWindow
->ClearStorageAllowedCache();
2429 // The storage objects contain the URL of the window. We have to
2430 // recreate them when the innerWindow is reused.
2431 newInnerWindow
->mLocalStorage
= nullptr;
2432 newInnerWindow
->mSessionStorage
= nullptr;
2433 newInnerWindow
->mPerformance
= nullptr;
2435 // This must be called after nullifying the internal objects because
2436 // here we could recreate them, calling the getter methods, and store
2437 // them into the JS slots. If we nullify them after, the slot values and
2438 // the objects will be out of sync.
2439 newInnerWindow
->ClearDocumentDependentSlots(cx
);
2441 newInnerWindow
->InitDocumentDependentState(cx
);
2443 // Initialize DOM classes etc on the inner window.
2444 JS::Rooted
<JSObject
*> obj(cx
, newInnerGlobal
);
2445 rv
= kungFuDeathGrip
->InitClasses(obj
);
2446 NS_ENSURE_SUCCESS(rv
, rv
);
2449 // When replacing an initial about:blank document we call
2450 // ExecutionReady again to update the client creation URL.
2451 rv
= newInnerWindow
->ExecutionReady();
2452 NS_ENSURE_SUCCESS(rv
, rv
);
2455 newInnerWindow
->DefineArgumentsProperty(mArguments
);
2456 mArguments
= nullptr;
2459 // Give the new inner window our chrome event handler (since it
2460 // doesn't have one).
2461 newInnerWindow
->mChromeEventHandler
= mChromeEventHandler
;
2464 if (!aState
&& reUseInnerWindow
) {
2465 // Notify our WindowGlobalChild that it has a new document. If `aState` was
2466 // passed, we're restoring the window from the BFCache, so the document
2468 // If we didn't have a window global child before, then initializing
2469 // it will have set all the required state, so we don't need to do
2471 mInnerWindow
->GetWindowGlobalChild()->OnNewDocument(aDocument
);
2474 // Update the current window for our BrowsingContext.
2475 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
2477 if (bc
->IsOwnedByProcess()) {
2478 MOZ_ALWAYS_SUCCEEDS(bc
->SetCurrentInnerWindowId(mInnerWindow
->WindowID()));
2481 // We no longer need the old inner window. Start its destruction if
2482 // its not being reused and clear our reference.
2483 if (doomCurrentInner
) {
2484 currentInner
->FreeInnerObjects();
2486 currentInner
= nullptr;
2488 // We wait to fire the debugger hook until the window is all set up and hooked
2489 // up with the outer. See bug 969156.
2490 if (createdInnerWindow
) {
2491 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2492 "nsGlobalWindowInner::FireOnNewGlobalObject", newInnerWindow
,
2493 &nsGlobalWindowInner::FireOnNewGlobalObject
));
2496 if (!newInnerWindow
->mHasNotifiedGlobalCreated
&& mDoc
) {
2497 // We should probably notify. However if this is the, arguably bad,
2498 // situation when we're creating a temporary non-chrome-about-blank
2499 // document in a chrome docshell, don't notify just yet. Instead wait
2500 // until we have a real chrome doc.
2501 const bool isContentAboutBlankInChromeDocshell
= [&] {
2506 RefPtr
<BrowsingContext
> bc
= mDocShell
->GetBrowsingContext();
2507 if (!bc
|| bc
->GetType() != BrowsingContext::Type::Chrome
) {
2511 return !mDoc
->NodePrincipal()->IsSystemPrincipal();
2514 if (!isContentAboutBlankInChromeDocshell
) {
2515 newInnerWindow
->mHasNotifiedGlobalCreated
= true;
2516 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2517 "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
2518 &nsGlobalWindowOuter::DispatchDOMWindowCreated
));
2522 PreloadLocalStorage();
2524 // Do this here rather than in say the Document constructor, since
2525 // we need a WindowContext available.
2526 mDoc
->InitUseCounters();
2532 void nsGlobalWindowOuter::PrepareForProcessChange(JSObject
* aProxy
) {
2533 JS::Rooted
<JSObject
*> localProxy(RootingCx(), aProxy
);
2534 MOZ_ASSERT(js::IsWindowProxy(localProxy
));
2536 RefPtr
<nsGlobalWindowOuter
> outerWindow
=
2537 nsOuterWindowProxy::GetOuterWindow(localProxy
);
2544 JSContext
* cx
= jsapi
.cx();
2546 JSAutoRealm
ar(cx
, localProxy
);
2548 // Clear out existing references from the browsing context and outer window to
2549 // the proxy, and from the proxy to the outer window. These references will
2550 // become invalid once the proxy is transplanted. Clearing the window proxy
2551 // from the browsing context is also necessary to indicate that it is for an
2552 // out of process window.
2553 outerWindow
->ClearWrapper(localProxy
);
2554 RefPtr
<BrowsingContext
> bc
= outerWindow
->GetBrowsingContext();
2556 MOZ_ASSERT(bc
->GetWindowProxy() == localProxy
);
2557 bc
->ClearWindowProxy();
2558 js::SetProxyReservedSlot(localProxy
, OUTER_WINDOW_SLOT
,
2559 JS::PrivateValue(nullptr));
2560 js::SetProxyReservedSlot(localProxy
, HOLDER_WEAKMAP_SLOT
,
2561 JS::UndefinedValue());
2563 // Create a new remote outer window proxy, and transplant to it.
2564 JS::Rooted
<JSObject
*> remoteProxy(cx
);
2566 if (!mozilla::dom::GetRemoteOuterWindowProxy(cx
, bc
, localProxy
,
2568 MOZ_CRASH("PrepareForProcessChange GetRemoteOuterWindowProxy");
2571 if (!xpc::TransplantObjectNukingXrayWaiver(cx
, localProxy
, remoteProxy
)) {
2572 MOZ_CRASH("PrepareForProcessChange TransplantObject");
2576 void nsGlobalWindowOuter::PreloadLocalStorage() {
2577 if (!Storage::StoragePrefIsEnabled()) {
2581 if (IsChromeWindow()) {
2585 nsIPrincipal
* principal
= GetPrincipal();
2586 nsIPrincipal
* storagePrincipal
= GetEffectiveStoragePrincipal();
2587 if (!principal
|| !storagePrincipal
) {
2593 nsCOMPtr
<nsIDOMStorageManager
> storageManager
=
2594 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv
);
2595 if (NS_FAILED(rv
)) {
2599 // private browsing windows do not persist local storage to disk so we should
2600 // only try to precache storage when we're not a private browsing window.
2601 if (!principal
->GetIsInPrivateBrowsing()) {
2602 RefPtr
<Storage
> storage
;
2603 rv
= storageManager
->PrecacheStorage(principal
, storagePrincipal
,
2604 getter_AddRefs(storage
));
2605 if (NS_SUCCEEDED(rv
)) {
2606 mLocalStorage
= storage
;
2611 void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
2616 // Fire DOMWindowCreated at chrome event listeners
2617 nsContentUtils::DispatchChromeEvent(mDoc
, mDoc
, u
"DOMWindowCreated"_ns
,
2618 CanBubble::eYes
, Cancelable::eNo
);
2620 nsCOMPtr
<nsIObserverService
> observerService
=
2621 mozilla::services::GetObserverService();
2623 // The event dispatching could possibly cause docshell destory, and
2624 // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(),
2625 // so check it again here.
2626 if (observerService
&& mDoc
) {
2627 nsAutoString origin
;
2628 nsIPrincipal
* principal
= mDoc
->NodePrincipal();
2629 nsContentUtils::GetWebExposedOriginSerialization(principal
, origin
);
2630 observerService
->NotifyObservers(static_cast<nsIDOMWindow
*>(this),
2631 principal
->IsSystemPrincipal()
2632 ? "chrome-document-global-created"
2633 : "content-document-global-created",
2638 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(u
""_ns
); }
2640 void nsGlobalWindowOuter::SetDocShell(nsDocShell
* aDocShell
) {
2641 MOZ_ASSERT(aDocShell
);
2643 if (aDocShell
== mDocShell
) {
2647 mDocShell
= aDocShell
;
2648 mBrowsingContext
= aDocShell
->GetBrowsingContext();
2650 RefPtr
<BrowsingContext
> parentContext
= mBrowsingContext
->GetParent();
2652 MOZ_RELEASE_ASSERT(!parentContext
||
2653 GetBrowsingContextGroup() == parentContext
->Group());
2655 mTopLevelOuterContentWindow
= mBrowsingContext
->IsTopContent();
2657 // Get our enclosing chrome shell and retrieve its global window impl, so
2658 // that we can do some forwarding to the chrome document.
2659 RefPtr
<EventTarget
> chromeEventHandler
;
2660 mDocShell
->GetChromeEventHandler(getter_AddRefs(chromeEventHandler
));
2661 mChromeEventHandler
= chromeEventHandler
;
2662 if (!mChromeEventHandler
) {
2663 // We have no chrome event handler. If we have a parent,
2664 // get our chrome event handler from the parent. If
2665 // we don't have a parent, then we need to make a new
2666 // window root object that will function as a chrome event
2667 // handler and receive all events that occur anywhere inside
2669 nsCOMPtr
<nsPIDOMWindowOuter
> parentWindow
= GetInProcessParent();
2670 if (parentWindow
.get() != this) {
2671 mChromeEventHandler
= parentWindow
->GetChromeEventHandler();
2673 mChromeEventHandler
= NS_NewWindowRoot(this);
2674 mIsRootOuterWindow
= true;
2678 SetIsBackgroundInternal(!mBrowsingContext
->IsActive());
2681 void nsGlobalWindowOuter::DetachFromDocShell(bool aIsBeingDiscarded
) {
2682 // DetachFromDocShell means the window is being torn down. Drop our
2683 // reference to the script context, allowing it to be deleted
2684 // later. Meanwhile, keep our weak reference to the script object
2685 // so that it can be retrieved later (until it is finalized by the JS GC).
2687 // Call FreeInnerObjects on all inner windows, not just the current
2688 // one, since some could be held by WindowStateHolder objects that
2690 RefPtr
<nsGlobalWindowInner
> inner
;
2691 for (PRCList
* node
= PR_LIST_HEAD(this); node
!= this;
2692 node
= PR_NEXT_LINK(inner
)) {
2693 // This cast is safe because `node != this`. Non-this nodes are inner
2695 inner
= static_cast<nsGlobalWindowInner
*>(node
);
2696 MOZ_ASSERT(!inner
->mOuterWindow
|| inner
->mOuterWindow
== this);
2697 inner
->FreeInnerObjects();
2700 // Don't report that we were detached to the nsWindowMemoryReporter, as it
2701 // only tracks inner windows.
2703 NotifyWindowIDDestroyed("outer-window-destroyed");
2705 nsGlobalWindowInner
* currentInner
= GetCurrentInnerWindowInternal(this);
2708 NS_ASSERTION(mDoc
, "Must have doc!");
2710 // Remember the document's principal and URI.
2711 mDocumentPrincipal
= mDoc
->NodePrincipal();
2712 mDocumentCookiePrincipal
= mDoc
->EffectiveCookiePrincipal();
2713 mDocumentStoragePrincipal
= mDoc
->EffectiveStoragePrincipal();
2714 mDocumentPartitionedPrincipal
= mDoc
->PartitionedPrincipal();
2715 mDocumentURI
= mDoc
->GetDocumentURI();
2717 // Release our document reference
2718 DropOuterWindowDocs();
2723 mChromeEventHandler
= nullptr; // force release now
2726 // When we're about to destroy a top level content window
2727 // (for example a tab), we trigger a full GC by passing null as the last
2728 // param. We also trigger a full GC for chrome windows.
2729 nsJSContext::PokeGC(JS::GCReason::SET_DOC_SHELL
,
2730 (mTopLevelOuterContentWindow
|| mIsChrome
)
2732 : GetWrapperPreserveColor());
2736 if (aIsBeingDiscarded
) {
2737 // If our BrowsingContext is being discarded, make a note that our current
2738 // inner window was active at the time it went away.
2739 if (nsGlobalWindowInner
* currentInner
=
2740 GetCurrentInnerWindowInternal(this)) {
2741 currentInner
->SetWasCurrentInnerWindow();
2745 mDocShell
= nullptr;
2746 mBrowsingContext
->ClearDocShell();
2751 void nsGlobalWindowOuter::UpdateParentTarget() {
2752 // NOTE: This method is nearly identical to
2753 // nsGlobalWindowInner::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2754 // UPDATE THE OTHER ONE TOO! The one difference is that this method updates
2755 // mMessageManager as well, which inner windows don't have.
2757 // Try to get our frame element's tab child global (its in-process message
2758 // manager). If that fails, fall back to the chrome event handler's tab
2759 // child global, and if it doesn't have one, just use the chrome event
2762 nsCOMPtr
<Element
> frameElement
= GetFrameElementInternal();
2763 mMessageManager
= nsContentUtils::TryGetBrowserChildGlobal(frameElement
);
2765 if (!mMessageManager
) {
2766 nsGlobalWindowOuter
* topWin
= GetInProcessScriptableTopInternal();
2768 frameElement
= topWin
->GetFrameElementInternal();
2769 mMessageManager
= nsContentUtils::TryGetBrowserChildGlobal(frameElement
);
2773 if (!mMessageManager
) {
2775 nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler
);
2778 if (mMessageManager
) {
2779 mParentTarget
= mMessageManager
;
2781 mParentTarget
= mChromeEventHandler
;
2785 EventTarget
* nsGlobalWindowOuter::GetTargetForEventTargetChain() {
2786 return GetCurrentInnerWindowInternal(this);
2789 void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
2790 MOZ_CRASH("The outer window should not be part of an event path");
2793 bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() {
2794 if (!nsContentUtils::GetCurrentJSContext()) {
2795 return false; // non-scripted caller.
2798 BrowsingContextGroup
* group
= GetBrowsingContextGroup();
2803 return group
->DialogsAreBeingAbused();
2806 bool nsGlobalWindowOuter::AreDialogsEnabled() {
2807 BrowsingContextGroup
* group
= mBrowsingContext
->Group();
2809 NS_ERROR("AreDialogsEnabled() called without a browsing context group?");
2813 // Dialogs are blocked if the content viewer is hidden
2815 nsCOMPtr
<nsIDocumentViewer
> viewer
;
2816 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
2819 viewer
->GetIsHidden(&isHidden
);
2825 // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS
2826 // (or if we have no document, of course). Which document? Who knows; the
2827 // spec is daft. See <https://github.com/whatwg/html/issues/1206>. For now
2828 // just go ahead and check mDoc, since in everything except edge cases in
2829 // which a frame is allow-same-origin but not allow-scripts and is being poked
2830 // at by some other window this should be the right thing anyway.
2831 if (!mDoc
|| (mDoc
->GetSandboxFlags() & SANDBOXED_MODALS
)) {
2835 return group
->GetAreDialogsEnabled();
2838 void nsGlobalWindowOuter::DisableDialogs() {
2839 BrowsingContextGroup
* group
= mBrowsingContext
->Group();
2841 NS_ERROR("DisableDialogs() called without a browsing context group?");
2846 group
->SetAreDialogsEnabled(false);
2850 void nsGlobalWindowOuter::EnableDialogs() {
2851 BrowsingContextGroup
* group
= mBrowsingContext
->Group();
2853 NS_ERROR("EnableDialogs() called without a browsing context group?");
2858 group
->SetAreDialogsEnabled(true);
2862 nsresult
nsGlobalWindowOuter::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
2863 MOZ_CRASH("The outer window should not be part of an event path");
2866 void nsGlobalWindowOuter::PoisonOuterWindowProxy(JSObject
* aObject
) {
2867 if (aObject
== GetWrapperMaybeDead()) {
2872 nsresult
nsGlobalWindowOuter::SetArguments(nsIArray
* aArguments
) {
2875 // We've now mostly separated them, but the difference is still opaque to
2876 // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
2877 // embedding waltz we do here).
2879 // So we need to demultiplex the two cases here.
2880 nsGlobalWindowInner
* currentInner
= GetCurrentInnerWindowInternal(this);
2882 mArguments
= aArguments
;
2883 rv
= currentInner
->DefineArgumentsProperty(aArguments
);
2884 NS_ENSURE_SUCCESS(rv
, rv
);
2889 //*****************************************************************************
2890 // nsGlobalWindowOuter::nsIScriptObjectPrincipal
2891 //*****************************************************************************
2893 nsIPrincipal
* nsGlobalWindowOuter::GetPrincipal() {
2895 // If we have a document, get the principal from the document
2896 return mDoc
->NodePrincipal();
2899 if (mDocumentPrincipal
) {
2900 return mDocumentPrincipal
;
2903 // If we don't have a principal and we don't have a document we
2904 // ask the parent window for the principal. This can happen when
2905 // loading a frameset that has a <frame src="javascript:xxx">, in
2906 // that case the global window is used in JS before we've loaded
2907 // a document into the window.
2909 nsCOMPtr
<nsIScriptObjectPrincipal
> objPrincipal
=
2910 do_QueryInterface(GetInProcessParentInternal());
2913 return objPrincipal
->GetPrincipal();
2919 nsIPrincipal
* nsGlobalWindowOuter::GetEffectiveCookiePrincipal() {
2921 // If we have a document, get the principal from the document
2922 return mDoc
->EffectiveCookiePrincipal();
2925 if (mDocumentCookiePrincipal
) {
2926 return mDocumentCookiePrincipal
;
2929 // If we don't have a cookie principal and we don't have a document we ask
2930 // the parent window for the cookie principal.
2932 nsCOMPtr
<nsIScriptObjectPrincipal
> objPrincipal
=
2933 do_QueryInterface(GetInProcessParentInternal());
2936 return objPrincipal
->GetEffectiveCookiePrincipal();
2942 nsIPrincipal
* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
2944 // If we have a document, get the principal from the document
2945 return mDoc
->EffectiveStoragePrincipal();
2948 if (mDocumentStoragePrincipal
) {
2949 return mDocumentStoragePrincipal
;
2952 // If we don't have a storage principal and we don't have a document we ask
2953 // the parent window for the storage principal.
2955 nsCOMPtr
<nsIScriptObjectPrincipal
> objPrincipal
=
2956 do_QueryInterface(GetInProcessParentInternal());
2959 return objPrincipal
->GetEffectiveStoragePrincipal();
2965 nsIPrincipal
* nsGlobalWindowOuter::PartitionedPrincipal() {
2967 // If we have a document, get the principal from the document
2968 return mDoc
->PartitionedPrincipal();
2971 if (mDocumentPartitionedPrincipal
) {
2972 return mDocumentPartitionedPrincipal
;
2975 // If we don't have a partitioned principal and we don't have a document we
2976 // ask the parent window for the partitioned principal.
2978 nsCOMPtr
<nsIScriptObjectPrincipal
> objPrincipal
=
2979 do_QueryInterface(GetInProcessParentInternal());
2982 return objPrincipal
->PartitionedPrincipal();
2988 //*****************************************************************************
2989 // nsGlobalWindowOuter::nsIDOMWindow
2990 //*****************************************************************************
2992 Element
* nsPIDOMWindowOuter::GetFrameElementInternal() const {
2993 return mFrameElement
;
2996 void nsPIDOMWindowOuter::SetFrameElementInternal(Element
* aFrameElement
) {
2997 mFrameElement
= aFrameElement
;
3000 Navigator
* nsGlobalWindowOuter::GetNavigator() {
3001 FORWARD_TO_INNER(Navigator
, (), nullptr);
3004 nsScreen
* nsGlobalWindowOuter::GetScreen() {
3005 FORWARD_TO_INNER(Screen
, (), nullptr);
3008 void nsPIDOMWindowOuter::ActivateMediaComponents() {
3009 if (!ShouldDelayMediaFromStart()) {
3012 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
3013 ("nsPIDOMWindowOuter, ActiveMediaComponents, "
3014 "no longer to delay media from start, this = %p\n",
3016 if (BrowsingContext
* bc
= GetBrowsingContext()) {
3017 Unused
<< bc
->Top()->SetShouldDelayMediaFromStart(false);
3019 NotifyResumingDelayedMedia();
3022 bool nsPIDOMWindowOuter::ShouldDelayMediaFromStart() const {
3023 BrowsingContext
* bc
= GetBrowsingContext();
3024 return bc
&& bc
->Top()->GetShouldDelayMediaFromStart();
3027 void nsPIDOMWindowOuter::NotifyResumingDelayedMedia() {
3028 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
3030 service
->NotifyResumingDelayedMedia(this);
3034 bool nsPIDOMWindowOuter::GetAudioMuted() const {
3035 BrowsingContext
* bc
= GetBrowsingContext();
3036 return bc
&& bc
->Top()->GetMuted();
3039 void nsPIDOMWindowOuter::RefreshMediaElementsVolume() {
3040 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
3042 // TODO: RefreshAgentsVolume can probably be simplified further.
3043 service
->RefreshAgentsVolume(this, 1.0f
, GetAudioMuted());
3047 mozilla::dom::BrowsingContextGroup
*
3048 nsPIDOMWindowOuter::GetBrowsingContextGroup() const {
3049 return mBrowsingContext
? mBrowsingContext
->Group() : nullptr;
3052 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::GetParentOuter() {
3053 BrowsingContext
* bc
= GetBrowsingContext();
3054 return bc
? bc
->GetParent(IgnoreErrors()) : nullptr;
3058 * GetInProcessScriptableParent used to be called when a script read
3059 * window.parent. Under Fission, that is now handled by
3060 * BrowsingContext::GetParent, and the result is a WindowProxyHolder rather than
3061 * an actual global window. This method still exists for legacy callers which
3062 * relied on the old logic, and require in-process windows. However, it only
3063 * works correctly when no out-of-process frames exist between this window and
3064 * the top-level window, so it should not be used in new code.
3066 * In contrast to GetRealParent, GetInProcessScriptableParent respects <iframe
3067 * mozbrowser> boundaries, so if |this| is contained by an <iframe
3068 * mozbrowser>, we will return |this| as its own parent.
3070 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetInProcessScriptableParent() {
3075 if (BrowsingContext
* parentBC
= GetBrowsingContext()->GetParent()) {
3076 if (nsCOMPtr
<nsPIDOMWindowOuter
> parent
= parentBC
->GetDOMWindow()) {
3084 * Behavies identically to GetInProcessScriptableParent extept that it returns
3085 * null if GetInProcessScriptableParent would return this window.
3087 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetInProcessScriptableParentOrNull() {
3088 nsPIDOMWindowOuter
* parent
= GetInProcessScriptableParent();
3089 return (nsGlobalWindowOuter::Cast(parent
) == this) ? nullptr : parent
;
3092 already_AddRefed
<nsPIDOMWindowOuter
> nsGlobalWindowOuter::GetInProcessParent() {
3097 if (auto* parentBC
= GetBrowsingContext()->GetParent()) {
3098 if (auto* parent
= parentBC
->GetDOMWindow()) {
3099 return do_AddRef(parent
);
3102 return do_AddRef(this);
3105 static nsresult
GetTopImpl(nsGlobalWindowOuter
* aWin
, nsIURI
* aURIBeingLoaded
,
3106 nsPIDOMWindowOuter
** aTop
, bool aScriptable
,
3107 bool aExcludingExtensionAccessibleContentFrames
) {
3110 MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames
, !aScriptable
);
3112 // Walk up the parent chain.
3114 nsCOMPtr
<nsPIDOMWindowOuter
> prevParent
= aWin
;
3115 nsCOMPtr
<nsPIDOMWindowOuter
> parent
= aWin
;
3121 prevParent
= parent
;
3124 parent
= parent
->GetInProcessScriptableParent();
3126 parent
= parent
->GetInProcessParent();
3129 if (aExcludingExtensionAccessibleContentFrames
) {
3130 if (auto* p
= nsGlobalWindowOuter::Cast(parent
)) {
3131 nsGlobalWindowInner
* currentInner
= GetCurrentInnerWindowInternal(p
);
3132 nsIURI
* uri
= prevParent
->GetDocumentURI();
3134 // If our parent doesn't have a URI yet, we have a document that is in
3135 // the process of being loaded. In that case, our caller is
3136 // responsible for passing in the URI for the document that is being
3137 // loaded, so we fall back to using that URI here.
3138 uri
= aURIBeingLoaded
;
3141 if (currentInner
&& uri
) {
3142 // If we find an inner window, we better find the uri for the current
3143 // window we're looking at. If we can't find it directly, it is the
3144 // responsibility of our caller to provide it to us.
3145 MOZ_DIAGNOSTIC_ASSERT(uri
);
3147 // If the new parent has permission to load the current page, we're
3148 // at a moz-extension:// frame which has a host permission that allows
3149 // it to load the document that we've loaded. In that case, stop at
3150 // this frame and consider it the top-level frame.
3152 // Note that it's possible for the set of URIs accepted by
3153 // AddonAllowsLoad() to change at runtime, but we don't need to cache
3154 // the result of this check, since the important consumer of this code
3155 // (which is nsIHttpChannelInternal.topWindowURI) already caches the
3156 // result after computing it the first time.
3157 if (BasePrincipal::Cast(p
->GetPrincipal())
3158 ->AddonAllowsLoad(uri
, true)) {
3159 parent
= prevParent
;
3166 } while (parent
!= prevParent
);
3176 * GetInProcessScriptableTop used to be called when a script read window.top.
3177 * Under Fission, that is now handled by BrowsingContext::Top, and the result is
3178 * a WindowProxyHolder rather than an actual global window. This method still
3179 * exists for legacy callers which relied on the old logic, and require
3180 * in-process windows. However, it only works correctly when no out-of-process
3181 * frames exist between this window and the top-level window, so it should not
3182 * be used in new code.
3184 * In contrast to GetRealTop, GetInProcessScriptableTop respects <iframe
3185 * mozbrowser> boundaries. If we encounter a window owned by an <iframe
3186 * mozbrowser> while walking up the window hierarchy, we'll stop and return that
3189 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetInProcessScriptableTop() {
3190 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3191 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window
),
3192 /* aScriptable = */ true,
3193 /* aExcludingExtensionAccessibleContentFrames = */ false);
3194 return window
.get();
3197 already_AddRefed
<nsPIDOMWindowOuter
> nsGlobalWindowOuter::GetInProcessTop() {
3198 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3199 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window
),
3200 /* aScriptable = */ false,
3201 /* aExcludingExtensionAccessibleContentFrames = */ false);
3202 return window
.forget();
3205 already_AddRefed
<nsPIDOMWindowOuter
>
3206 nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames(
3207 nsIURI
* aURIBeingLoaded
) {
3208 // There is a parent-process equivalent of this in DocumentLoadListener.cpp
3209 // GetTopWindowExcludingExtensionAccessibleContentFrames
3210 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
3211 GetTopImpl(this, aURIBeingLoaded
, getter_AddRefs(window
),
3212 /* aScriptable = */ false,
3213 /* aExcludingExtensionAccessibleContentFrames = */ true);
3214 return window
.forget();
3217 void nsGlobalWindowOuter::GetContentOuter(JSContext
* aCx
,
3218 JS::MutableHandle
<JSObject
*> aRetval
,
3219 CallerType aCallerType
,
3220 ErrorResult
& aError
) {
3221 RefPtr
<BrowsingContext
> content
= GetContentInternal(aCallerType
, aError
);
3222 if (aError
.Failed()) {
3227 aRetval
.set(nullptr);
3231 JS::Rooted
<JS::Value
> val(aCx
);
3232 if (!ToJSValue(aCx
, WindowProxyHolder
{content
}, &val
)) {
3233 aError
.Throw(NS_ERROR_UNEXPECTED
);
3237 MOZ_ASSERT(val
.isObjectOrNull());
3238 aRetval
.set(val
.toObjectOrNull());
3241 already_AddRefed
<BrowsingContext
> nsGlobalWindowOuter::GetContentInternal(
3242 CallerType aCallerType
, ErrorResult
& aError
) {
3243 // First check for a named frame named "content"
3244 if (RefPtr
<BrowsingContext
> named
= GetChildWindow(u
"content"_ns
)) {
3245 return named
.forget();
3248 // If we're in the parent process, and being called by system code, `content`
3249 // should return the current primary content frame (if it's in-process).
3251 // We return `nullptr` if the current primary content frame is out-of-process,
3252 // rather than a remote window proxy, as that is the existing behaviour as of
3254 if (XRE_IsParentProcess() && aCallerType
== CallerType::System
) {
3255 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
= GetTreeOwner();
3257 aError
.Throw(NS_ERROR_FAILURE
);
3261 nsCOMPtr
<nsIDocShellTreeItem
> primaryContent
;
3262 treeOwner
->GetPrimaryContentShell(getter_AddRefs(primaryContent
));
3263 if (!primaryContent
) {
3267 return do_AddRef(primaryContent
->GetBrowsingContext());
3270 // For legacy untrusted callers we always return the same value as
3272 if (mDoc
&& aCallerType
!= CallerType::System
) {
3273 mDoc
->WarnOnceAbout(DeprecatedOperations::eWindowContentUntrusted
);
3276 MOZ_ASSERT(mBrowsingContext
->IsContent());
3277 return do_AddRef(mBrowsingContext
->Top());
3280 nsresult
nsGlobalWindowOuter::GetPrompter(nsIPrompt
** aPrompt
) {
3281 if (!mDocShell
) return NS_ERROR_FAILURE
;
3283 nsCOMPtr
<nsIPrompt
> prompter(do_GetInterface(mDocShell
));
3284 NS_ENSURE_TRUE(prompter
, NS_ERROR_NO_INTERFACE
);
3286 prompter
.forget(aPrompt
);
3290 bool nsGlobalWindowOuter::GetClosedOuter() {
3291 // If someone called close(), or if we don't have a docshell, we're closed.
3292 return mIsClosed
|| !mDocShell
;
3295 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); }
3297 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::IndexedGetterOuter(
3299 BrowsingContext
* bc
= GetBrowsingContext();
3300 NS_ENSURE_TRUE(bc
, nullptr);
3302 Span
<RefPtr
<BrowsingContext
>> children
= bc
->NonSyntheticChildren();
3304 if (aIndex
< children
.Length()) {
3305 return WindowProxyHolder(children
[aIndex
]);
3310 nsIControllers
* nsGlobalWindowOuter::GetControllersOuter(ErrorResult
& aError
) {
3311 if (!mControllers
) {
3312 mControllers
= new nsXULControllers();
3313 if (!mControllers
) {
3314 aError
.Throw(NS_ERROR_FAILURE
);
3318 // Add in the default controller
3319 RefPtr
<nsBaseCommandController
> commandController
=
3320 nsBaseCommandController::CreateWindowController();
3321 if (!commandController
) {
3322 aError
.Throw(NS_ERROR_FAILURE
);
3326 mControllers
->InsertControllerAt(0, commandController
);
3327 commandController
->SetCommandContext(static_cast<nsIDOMWindow
*>(this));
3330 return mControllers
;
3333 nsresult
nsGlobalWindowOuter::GetControllers(nsIControllers
** aResult
) {
3334 FORWARD_TO_INNER(GetControllers
, (aResult
), NS_ERROR_UNEXPECTED
);
3337 already_AddRefed
<BrowsingContext
>
3338 nsGlobalWindowOuter::GetOpenerBrowsingContext() {
3339 RefPtr
<BrowsingContext
> opener
= GetBrowsingContext()->GetOpener();
3340 MOZ_DIAGNOSTIC_ASSERT(!opener
||
3341 opener
->Group() == GetBrowsingContext()->Group());
3342 if (!opener
|| opener
->Group() != GetBrowsingContext()->Group()) {
3346 // Catch the case where we're chrome but the opener is not...
3347 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode() &&
3348 GetPrincipal() == nsContentUtils::GetSystemPrincipal()) {
3349 auto* openerWin
= nsGlobalWindowOuter::Cast(opener
->GetDOMWindow());
3351 openerWin
->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) {
3356 return opener
.forget();
3359 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetSameProcessOpener() {
3360 if (RefPtr
<BrowsingContext
> opener
= GetOpenerBrowsingContext()) {
3361 return opener
->GetDOMWindow();
3366 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::GetOpenerWindowOuter() {
3367 if (RefPtr
<BrowsingContext
> opener
= GetOpenerBrowsingContext()) {
3368 return WindowProxyHolder(std::move(opener
));
3373 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::GetOpener() {
3374 return GetOpenerWindowOuter();
3377 void nsGlobalWindowOuter::GetStatusOuter(nsAString
& aStatus
) {
3381 void nsGlobalWindowOuter::SetStatusOuter(const nsAString
& aStatus
) {
3384 // We don't support displaying window.status in the UI, so there's nothing
3388 void nsGlobalWindowOuter::GetNameOuter(nsAString
& aName
) {
3390 mDocShell
->GetName(aName
);
3394 void nsGlobalWindowOuter::SetNameOuter(const nsAString
& aName
,
3395 mozilla::ErrorResult
& aError
) {
3397 aError
= mDocShell
->SetName(aName
);
3401 // NOTE: The idea of this function is that it should return the same as
3402 // nsPresContext::CSSToDeviceScale() if it was in aWindow synchronously. For
3403 // that, we use the UnscaledDevicePixelsPerCSSPixel() (which contains the device
3404 // scale and the OS zoom scale) and then account for the browsing context full
3405 // zoom. See the declaration of this function for context about why this is
3407 CSSToLayoutDeviceScale
nsGlobalWindowOuter::CSSToDevScaleForBaseWindow(
3408 nsIBaseWindow
* aWindow
) {
3409 MOZ_ASSERT(aWindow
);
3410 auto scale
= aWindow
->UnscaledDevicePixelsPerCSSPixel();
3411 if (mBrowsingContext
) {
3412 scale
.scale
*= mBrowsingContext
->FullZoom();
3417 nsresult
nsGlobalWindowOuter::GetInnerSize(CSSSize
& aSize
) {
3418 EnsureSizeAndPositionUpToDate();
3420 NS_ENSURE_STATE(mDocShell
);
3422 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
3423 PresShell
* presShell
= mDocShell
->GetPresShell();
3425 if (!presContext
|| !presShell
) {
3430 // Whether or not the css viewport has been overridden, we can get the
3431 // correct value by looking at the visible area of the presContext.
3432 if (RefPtr
<nsViewManager
> viewManager
= presShell
->GetViewManager()) {
3433 viewManager
->FlushDelayedResize();
3436 // FIXME: Bug 1598487 - Return the layout viewport instead of the ICB.
3437 nsSize viewportSize
= presContext
->GetVisibleArea().Size();
3438 if (presContext
->GetDynamicToolbarState() == DynamicToolbarState::Collapsed
) {
3440 nsLayoutUtils::ExpandHeightForViewportUnits(presContext
, viewportSize
);
3443 aSize
= CSSPixel::FromAppUnits(viewportSize
);
3445 switch (StaticPrefs::dom_innerSize_rounding()) {
3447 aSize
.width
= std::roundf(aSize
.width
);
3448 aSize
.height
= std::roundf(aSize
.height
);
3451 aSize
.width
= std::truncf(aSize
.width
);
3452 aSize
.height
= std::truncf(aSize
.height
);
3461 double nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult
& aError
) {
3463 aError
= GetInnerSize(size
);
3467 nsresult
nsGlobalWindowOuter::GetInnerWidth(double* aInnerWidth
) {
3468 FORWARD_TO_INNER(GetInnerWidth
, (aInnerWidth
), NS_ERROR_UNEXPECTED
);
3471 double nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult
& aError
) {
3473 aError
= GetInnerSize(size
);
3477 nsresult
nsGlobalWindowOuter::GetInnerHeight(double* aInnerHeight
) {
3478 FORWARD_TO_INNER(GetInnerHeight
, (aInnerHeight
), NS_ERROR_UNEXPECTED
);
3481 CSSIntSize
nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType
,
3482 ErrorResult
& aError
) {
3483 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType
,
3484 RFPTarget::WindowOuterSize
)) {
3485 if (BrowsingContext
* bc
= GetBrowsingContext()) {
3486 return bc
->Top()->GetTopInnerSizeForRFP();
3491 // Windows showing documents in RDM panes and any subframes within them
3492 // return the simulated device size.
3494 Maybe
<CSSIntSize
> deviceSize
= GetRDMDeviceSize(*mDoc
);
3495 if (deviceSize
.isSome()) {
3500 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
3501 if (!treeOwnerAsWin
) {
3502 aError
.Throw(NS_ERROR_FAILURE
);
3506 return RoundedToInt(treeOwnerAsWin
->GetSize() /
3507 CSSToDevScaleForBaseWindow(treeOwnerAsWin
));
3510 int32_t nsGlobalWindowOuter::GetOuterWidthOuter(CallerType aCallerType
,
3511 ErrorResult
& aError
) {
3512 return GetOuterSize(aCallerType
, aError
).width
;
3515 int32_t nsGlobalWindowOuter::GetOuterHeightOuter(CallerType aCallerType
,
3516 ErrorResult
& aError
) {
3517 return GetOuterSize(aCallerType
, aError
).height
;
3520 CSSPoint
nsGlobalWindowOuter::ScreenEdgeSlop() {
3521 if (NS_WARN_IF(!mDocShell
)) {
3524 RefPtr
<nsPresContext
> pc
= mDocShell
->GetPresContext();
3525 if (NS_WARN_IF(!pc
)) {
3528 nsCOMPtr
<nsIWidget
> widget
= GetMainWidget();
3529 if (NS_WARN_IF(!widget
)) {
3532 LayoutDeviceIntPoint pt
= widget
->GetScreenEdgeSlop();
3534 LayoutDeviceIntPoint::ToAppUnits(pt
, pc
->AppUnitsPerDevPixel());
3535 return CSSPoint::FromAppUnits(auPoint
);
3538 CSSIntPoint
nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType
,
3539 ErrorResult
& aError
) {
3540 // When resisting fingerprinting, always return (0,0)
3541 if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType
,
3542 RFPTarget::WindowScreenXY
)) {
3543 return CSSIntPoint(0, 0);
3546 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
3547 if (!treeOwnerAsWin
) {
3548 aError
.Throw(NS_ERROR_FAILURE
);
3549 return CSSIntPoint(0, 0);
3552 LayoutDeviceIntPoint windowPos
;
3553 aError
= treeOwnerAsWin
->GetPosition(&windowPos
.x
.value
, &windowPos
.y
.value
);
3555 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
3557 // XXX Fishy LayoutDevice to CSS conversion?
3558 return CSSIntPoint(windowPos
.x
, windowPos
.y
);
3561 nsDeviceContext
* context
= presContext
->DeviceContext();
3562 auto windowPosAppUnits
= LayoutDeviceIntPoint::ToAppUnits(
3563 windowPos
, context
->AppUnitsPerDevPixel());
3564 return CSSIntPoint::FromAppUnitsRounded(windowPosAppUnits
);
3567 int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType
,
3568 ErrorResult
& aError
) {
3569 return GetScreenXY(aCallerType
, aError
).x
;
3572 nsRect
nsGlobalWindowOuter::GetInnerScreenRect() {
3577 EnsureSizeAndPositionUpToDate();
3583 PresShell
* presShell
= mDocShell
->GetPresShell();
3587 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
3592 return rootFrame
->GetScreenRectInAppUnits();
3595 Maybe
<CSSIntSize
> nsGlobalWindowOuter::GetRDMDeviceSize(
3596 const Document
& aDocument
) {
3597 // RDM device size should reflect the simulated device resolution, and
3598 // be independent of any full zoom or resolution zoom applied to the
3599 // content. To get this value, we get the "unscaled" browser child size,
3600 // and divide by the full zoom. "Unscaled" in this case means unscaled
3601 // from device to screen but it has been affected (multiplied) by the
3602 // full zoom and we need to compensate for that.
3603 MOZ_RELEASE_ASSERT(NS_IsMainThread());
3605 // Bug 1576256: This does not work for cross-process subframes.
3606 const Document
* topInProcessContentDoc
=
3607 aDocument
.GetTopLevelContentDocumentIfSameProcess();
3608 BrowsingContext
* bc
= topInProcessContentDoc
3609 ? topInProcessContentDoc
->GetBrowsingContext()
3611 if (bc
&& bc
->InRDMPane()) {
3612 nsIDocShell
* docShell
= topInProcessContentDoc
->GetDocShell();
3614 nsPresContext
* presContext
= docShell
->GetPresContext();
3616 nsCOMPtr
<nsIBrowserChild
> child
= docShell
->GetBrowserChild();
3618 // We intentionally use GetFullZoom here instead of
3619 // GetDeviceFullZoom, because the unscaledInnerSize is based
3620 // on the full zoom and not the device full zoom (which is
3621 // rounded to result in integer device pixels).
3622 float zoom
= presContext
->GetFullZoom();
3623 BrowserChild
* bc
= static_cast<BrowserChild
*>(child
.get());
3624 CSSSize unscaledSize
= bc
->GetUnscaledInnerSize();
3625 return Some(CSSIntSize(gfx::RoundedToInt(unscaledSize
/ zoom
)));
3633 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType
) {
3634 // When resisting fingerprinting, always return 0.
3635 if (nsIGlobalObject::ShouldResistFingerprinting(
3636 aCallerType
, RFPTarget::WindowInnerScreenXY
)) {
3640 nsRect r
= GetInnerScreenRect();
3641 return nsPresContext::AppUnitsToFloatCSSPixels(r
.x
);
3644 float nsGlobalWindowOuter::GetMozInnerScreenYOuter(CallerType aCallerType
) {
3645 // Return 0 to prevent fingerprinting.
3646 if (nsIGlobalObject::ShouldResistFingerprinting(
3647 aCallerType
, RFPTarget::WindowInnerScreenXY
)) {
3651 nsRect r
= GetInnerScreenRect();
3652 return nsPresContext::AppUnitsToFloatCSSPixels(r
.y
);
3655 int32_t nsGlobalWindowOuter::GetScreenYOuter(CallerType aCallerType
,
3656 ErrorResult
& aError
) {
3657 return GetScreenXY(aCallerType
, aError
).y
;
3660 // NOTE: Arguments to this function should have values scaled to
3661 // CSS pixels, not device pixels.
3662 void nsGlobalWindowOuter::CheckSecurityWidthAndHeight(int32_t* aWidth
,
3664 CallerType aCallerType
) {
3665 if (aCallerType
!= CallerType::System
) {
3666 // if attempting to resize the window, hide any open popups
3667 nsContentUtils::HidePopupsInDocument(mDoc
);
3670 // This one is easy. Just ensure the variable is greater than 100;
3671 if ((aWidth
&& *aWidth
< 100) || (aHeight
&& *aHeight
< 100)) {
3672 // Check security state for use in determing window dimensions
3674 if (aCallerType
!= CallerType::System
) {
3676 if (aWidth
&& *aWidth
< 100) {
3679 if (aHeight
&& *aHeight
< 100) {
3686 // NOTE: Arguments to this function should have values in app units
3687 void nsGlobalWindowOuter::SetCSSViewportWidthAndHeight(nscoord aInnerWidth
,
3688 nscoord aInnerHeight
) {
3689 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
3691 nsRect shellArea
= presContext
->GetVisibleArea();
3692 shellArea
.SetHeight(aInnerHeight
);
3693 shellArea
.SetWidth(aInnerWidth
);
3695 // FIXME(emilio): This doesn't seem to be ok, this doesn't reflow or
3696 // anything... Should go through PresShell::ResizeReflow.
3698 // But I don't think this can be reached by content, as we don't allow to set
3699 // inner{Width,Height}.
3700 presContext
->SetVisibleArea(shellArea
);
3703 // NOTE: Arguments to this function should have values scaled to
3704 // CSS pixels, not device pixels.
3705 void nsGlobalWindowOuter::CheckSecurityLeftAndTop(int32_t* aLeft
, int32_t* aTop
,
3706 CallerType aCallerType
) {
3707 // This one is harder. We have to get the screen size and window dimensions.
3709 // Check security state for use in determing window dimensions
3711 if (aCallerType
!= CallerType::System
) {
3712 // if attempting to move the window, hide any open popups
3713 nsContentUtils::HidePopupsInDocument(mDoc
);
3715 if (nsGlobalWindowOuter
* rootWindow
=
3716 nsGlobalWindowOuter::Cast(GetPrivateRoot())) {
3717 rootWindow
->FlushPendingNotifications(FlushType::Layout
);
3720 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
3722 RefPtr
<nsScreen
> screen
= GetScreen();
3724 if (treeOwnerAsWin
&& screen
) {
3725 CSSToLayoutDeviceScale scale
= CSSToDevScaleForBaseWindow(treeOwnerAsWin
);
3726 CSSIntRect winRect
=
3727 CSSIntRect::Round(treeOwnerAsWin
->GetPositionAndSize() / scale
);
3729 // Get the screen dimensions
3730 // XXX This should use nsIScreenManager once it's fully fleshed out.
3731 int32_t screenLeft
= screen
->AvailLeft();
3732 int32_t screenWidth
= screen
->AvailWidth();
3733 int32_t screenHeight
= screen
->AvailHeight();
3734 #if defined(XP_MACOSX)
3735 /* The mac's coordinate system is different from the assumed Windows'
3736 system. It offsets by the height of the menubar so that a window
3737 placed at (0,0) will be entirely visible. Unfortunately that
3738 correction is made elsewhere (in Widget) and the meaning of
3739 the Avail... coordinates is overloaded. Here we allow a window
3740 to be placed at (0,0) because it does make sense to do so.
3742 int32_t screenTop
= screen
->Top();
3744 int32_t screenTop
= screen
->AvailTop();
3748 if (screenLeft
+ screenWidth
< *aLeft
+ winRect
.width
)
3749 *aLeft
= screenLeft
+ screenWidth
- winRect
.width
;
3750 if (screenLeft
> *aLeft
) *aLeft
= screenLeft
;
3753 if (screenTop
+ screenHeight
< *aTop
+ winRect
.height
)
3754 *aTop
= screenTop
+ screenHeight
- winRect
.height
;
3755 if (screenTop
> *aTop
) *aTop
= screenTop
;
3758 if (aLeft
) *aLeft
= 0;
3759 if (aTop
) *aTop
= 0;
3764 int32_t nsGlobalWindowOuter::GetScrollBoundaryOuter(Side aSide
) {
3765 FlushPendingNotifications(FlushType::Layout
);
3766 if (ScrollContainerFrame
* sf
= GetScrollContainerFrame()) {
3767 return nsPresContext::AppUnitsToIntCSSPixels(
3768 sf
->GetScrollRange().Edge(aSide
));
3773 CSSPoint
nsGlobalWindowOuter::GetScrollXY(bool aDoFlush
) {
3775 FlushPendingNotifications(FlushType::Layout
);
3777 EnsureSizeAndPositionUpToDate();
3780 ScrollContainerFrame
* sf
= GetScrollContainerFrame();
3782 return CSSIntPoint(0, 0);
3785 nsPoint scrollPos
= sf
->GetScrollPosition();
3786 if (scrollPos
!= nsPoint(0, 0) && !aDoFlush
) {
3787 // Oh, well. This is the expensive case -- the window is scrolled and we
3788 // didn't actually flush yet. Repeat, but with a flush, since the content
3789 // may get shorter and hence our scroll position may decrease.
3790 return GetScrollXY(true);
3793 return CSSPoint::FromAppUnits(scrollPos
);
3796 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x
; }
3798 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y
; }
3800 uint32_t nsGlobalWindowOuter::Length() {
3801 BrowsingContext
* bc
= GetBrowsingContext();
3802 return bc
? bc
->NonSyntheticChildren().Length() : 0;
3805 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::GetTopOuter() {
3806 BrowsingContext
* bc
= GetBrowsingContext();
3807 return bc
? bc
->GetTop(IgnoreErrors()) : nullptr;
3810 already_AddRefed
<BrowsingContext
> nsGlobalWindowOuter::GetChildWindow(
3811 const nsAString
& aName
) {
3812 NS_ENSURE_TRUE(mBrowsingContext
, nullptr);
3813 NS_ENSURE_TRUE(mInnerWindow
, nullptr);
3814 NS_ENSURE_TRUE(mInnerWindow
->GetWindowGlobalChild(), nullptr);
3816 return do_AddRef(mBrowsingContext
->FindChildWithName(
3817 aName
, *mInnerWindow
->GetWindowGlobalChild()));
3820 bool nsGlobalWindowOuter::DispatchCustomEvent(
3821 const nsAString
& aEventName
, ChromeOnlyDispatch aChromeOnlyDispatch
) {
3822 bool defaultActionEnabled
= true;
3824 if (aChromeOnlyDispatch
== ChromeOnlyDispatch::eYes
) {
3825 nsContentUtils::DispatchEventOnlyToChrome(mDoc
, this, aEventName
,
3826 CanBubble::eYes
, Cancelable::eYes
,
3827 &defaultActionEnabled
);
3829 nsContentUtils::DispatchTrustedEvent(mDoc
, this, aEventName
,
3830 CanBubble::eYes
, Cancelable::eYes
,
3831 &defaultActionEnabled
);
3834 return defaultActionEnabled
;
3837 bool nsGlobalWindowOuter::DispatchResizeEvent(const CSSIntSize
& aSize
) {
3839 RefPtr
<Event
> domEvent
=
3840 mDoc
->CreateEvent(u
"CustomEvent"_ns
, CallerType::System
, res
);
3845 // We don't init the AutoJSAPI with ourselves because we don't want it
3846 // reporting errors to our onerror handlers.
3849 JSContext
* cx
= jsapi
.cx();
3850 JSAutoRealm
ar(cx
, GetWrapperPreserveColor());
3852 DOMWindowResizeEventDetail detail
;
3853 detail
.mWidth
= aSize
.width
;
3854 detail
.mHeight
= aSize
.height
;
3855 JS::Rooted
<JS::Value
> detailValue(cx
);
3856 if (!ToJSValue(cx
, detail
, &detailValue
)) {
3860 CustomEvent
* customEvent
= static_cast<CustomEvent
*>(domEvent
.get());
3861 customEvent
->InitCustomEvent(cx
, u
"DOMWindowResize"_ns
,
3862 /* aCanBubble = */ true,
3863 /* aCancelable = */ true, detailValue
);
3865 domEvent
->SetTrusted(true);
3866 domEvent
->WidgetEventPtr()->mFlags
.mOnlyChromeDispatch
= true;
3868 nsCOMPtr
<EventTarget
> target
= this;
3869 domEvent
->SetTarget(target
);
3871 return target
->DispatchEvent(*domEvent
, CallerType::System
, IgnoreErrors());
3874 bool nsGlobalWindowOuter::WindowExists(const nsAString
& aName
,
3875 bool aForceNoOpener
,
3876 bool aLookForCallerOnJSStack
) {
3877 MOZ_ASSERT(mDocShell
, "Must have docshell");
3879 if (aForceNoOpener
) {
3880 return aName
.LowerCaseEqualsLiteral("_self") ||
3881 aName
.LowerCaseEqualsLiteral("_top") ||
3882 aName
.LowerCaseEqualsLiteral("_parent");
3885 if (WindowGlobalChild
* wgc
= mInnerWindow
->GetWindowGlobalChild()) {
3886 return wgc
->FindBrowsingContextWithName(aName
, aLookForCallerOnJSStack
);
3891 already_AddRefed
<nsIWidget
> nsGlobalWindowOuter::GetMainWidget() {
3892 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
3894 nsCOMPtr
<nsIWidget
> widget
;
3896 if (treeOwnerAsWin
) {
3897 treeOwnerAsWin
->GetMainWidget(getter_AddRefs(widget
));
3900 return widget
.forget();
3903 nsIWidget
* nsGlobalWindowOuter::GetNearestWidget() const {
3904 nsIDocShell
* docShell
= GetDocShell();
3908 PresShell
* presShell
= docShell
->GetPresShell();
3912 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
3916 return rootFrame
->GetView()->GetNearestWidget(nullptr);
3919 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen
,
3920 mozilla::ErrorResult
& aError
) {
3922 SetFullscreenInternal(FullscreenReason::ForFullscreenMode
, aFullscreen
);
3925 nsresult
nsGlobalWindowOuter::SetFullScreen(bool aFullscreen
) {
3926 return SetFullscreenInternal(FullscreenReason::ForFullscreenMode
,
3930 static void FinishDOMFullscreenChange(Document
* aDoc
, bool aInDOMFullscreen
) {
3931 if (aInDOMFullscreen
) {
3932 // Ask the document to handle any pending DOM fullscreen change.
3933 if (!Document::HandlePendingFullscreenRequests(aDoc
)) {
3934 // If we don't end up having anything in fullscreen,
3935 // async request exiting fullscreen.
3936 Document::AsyncExitFullscreen(aDoc
);
3939 // If the window is leaving fullscreen state, also ask the document
3940 // to exit from DOM Fullscreen.
3941 Document::ExitFullscreenInDocTree(aDoc
);
3945 struct FullscreenTransitionDuration
{
3946 // The unit of the durations is millisecond
3947 uint16_t mFadeIn
= 0;
3948 uint16_t mFadeOut
= 0;
3949 bool IsSuppressed() const { return mFadeIn
== 0 && mFadeOut
== 0; }
3952 static void GetFullscreenTransitionDuration(
3953 bool aEnterFullscreen
, FullscreenTransitionDuration
* aDuration
) {
3954 const char* pref
= aEnterFullscreen
3955 ? "full-screen-api.transition-duration.enter"
3956 : "full-screen-api.transition-duration.leave";
3957 nsAutoCString prefValue
;
3958 Preferences::GetCString(pref
, prefValue
);
3959 if (!prefValue
.IsEmpty()) {
3960 sscanf(prefValue
.get(), "%hu%hu", &aDuration
->mFadeIn
,
3961 &aDuration
->mFadeOut
);
3965 class FullscreenTransitionTask
: public Runnable
{
3967 FullscreenTransitionTask(const FullscreenTransitionDuration
& aDuration
,
3968 nsGlobalWindowOuter
* aWindow
, bool aFullscreen
,
3969 nsIWidget
* aWidget
, nsISupports
* aTransitionData
)
3970 : mozilla::Runnable("FullscreenTransitionTask"),
3973 mTransitionData(aTransitionData
),
3974 mDuration(aDuration
),
3975 mStage(eBeforeToggle
),
3976 mFullscreen(aFullscreen
) {}
3978 NS_IMETHOD
Run() override
;
3981 ~FullscreenTransitionTask() override
= default;
3984 * The flow of fullscreen transition:
3986 * parent process | child process
3987 * ----------------------------------------------------------------
3989 * | request/exit fullscreen
3991 * BeforeToggle stage |
3993 * ToggleFullscreen stage *1 |----->
3994 * | HandleFullscreenRequests
3996 * <-----| MozAfterPaint event
3997 * AfterToggle stage *2 |
4001 * Note we also start a timer at *1 so that if we don't get MozAfterPaint
4002 * from the child process in time, we continue going to *2.
4005 // BeforeToggle stage happens before we enter or leave fullscreen
4006 // state. In this stage, the task triggers the pre-toggle fullscreen
4007 // transition on the widget.
4009 // ToggleFullscreen stage actually executes the fullscreen toggle,
4010 // and wait for the next paint on the content to continue.
4012 // AfterToggle stage happens after we toggle the fullscreen state.
4013 // In this stage, the task triggers the post-toggle fullscreen
4014 // transition on the widget.
4016 // End stage is triggered after the final transition finishes.
4020 class Observer final
: public nsIObserver
, public nsINamed
{
4026 explicit Observer(FullscreenTransitionTask
* aTask
) : mTask(aTask
) {}
4029 ~Observer() = default;
4031 RefPtr
<FullscreenTransitionTask
> mTask
;
4034 static const char* const kPaintedTopic
;
4036 RefPtr
<nsGlobalWindowOuter
> mWindow
;
4037 nsCOMPtr
<nsIWidget
> mWidget
;
4038 nsCOMPtr
<nsITimer
> mTimer
;
4039 nsCOMPtr
<nsISupports
> mTransitionData
;
4041 TimeStamp mFullscreenChangeStartTime
;
4042 FullscreenTransitionDuration mDuration
;
4047 const char* const FullscreenTransitionTask::kPaintedTopic
=
4048 "fullscreen-painted";
4051 FullscreenTransitionTask::Run() {
4052 Stage stage
= mStage
;
4053 mStage
= Stage(mStage
+ 1);
4054 if (MOZ_UNLIKELY(mWidget
->Destroyed())) {
4055 // If the widget has been destroyed before we get here, don't try to
4056 // do anything more. Just let it go and release ourselves.
4057 NS_WARNING("The widget to fullscreen has been destroyed");
4058 mWindow
->mIsInFullScreenTransition
= false;
4061 if (stage
== eBeforeToggle
) {
4062 PROFILER_MARKER_UNTYPED("Fullscreen transition start", DOM
);
4064 mWindow
->mIsInFullScreenTransition
= true;
4066 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
4067 NS_ENSURE_TRUE(obs
, NS_ERROR_FAILURE
);
4068 obs
->NotifyObservers(nullptr, "fullscreen-transition-start", nullptr);
4070 mWidget
->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle
,
4071 mDuration
.mFadeIn
, mTransitionData
,
4073 } else if (stage
== eToggleFullscreen
) {
4074 PROFILER_MARKER_UNTYPED("Fullscreen toggle start", DOM
);
4075 mFullscreenChangeStartTime
= TimeStamp::Now();
4076 // Toggle the fullscreen state on the widget
4077 if (!mWindow
->SetWidgetFullscreen(FullscreenReason::ForFullscreenAPI
,
4078 mFullscreen
, mWidget
)) {
4079 // Fail to setup the widget, call FinishFullscreenChange to
4080 // complete fullscreen change directly.
4081 mWindow
->FinishFullscreenChange(mFullscreen
);
4083 // Set observer for the next content paint.
4084 nsCOMPtr
<nsIObserver
> observer
= new Observer(this);
4085 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
4086 obs
->AddObserver(observer
, kPaintedTopic
, false);
4087 // There are several edge cases where we may never get the paint
4088 // notification, including:
4089 // 1. the window/tab is closed before the next paint;
4090 // 2. the user has switched to another tab before we get here.
4091 // Completely fixing those cases seems to be tricky, and since they
4092 // should rarely happen, it probably isn't worth to fix. Hence we
4093 // simply add a timeout here to ensure we never hang forever.
4094 // In addition, if the page is complicated or the machine is less
4095 // powerful, layout could take a long time, in which case, staying
4096 // in black screen for that long could hurt user experience even
4097 // more than exposing an intermediate state.
4099 Preferences::GetUint("full-screen-api.transition.timeout", 1000);
4100 NS_NewTimerWithObserver(getter_AddRefs(mTimer
), observer
, timeout
,
4101 nsITimer::TYPE_ONE_SHOT
);
4102 } else if (stage
== eAfterToggle
) {
4103 Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS
,
4104 mFullscreenChangeStartTime
);
4105 mWidget
->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle
,
4106 mDuration
.mFadeOut
, mTransitionData
,
4108 } else if (stage
== eEnd
) {
4109 PROFILER_MARKER_UNTYPED("Fullscreen transition end", DOM
);
4111 mWindow
->mIsInFullScreenTransition
= false;
4113 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
4114 NS_ENSURE_TRUE(obs
, NS_ERROR_FAILURE
);
4115 obs
->NotifyObservers(nullptr, "fullscreen-transition-end", nullptr);
4117 mWidget
->CleanupFullscreenTransition();
4122 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer
, nsIObserver
, nsINamed
)
4125 FullscreenTransitionTask::Observer::Observe(nsISupports
* aSubject
,
4127 const char16_t
* aData
) {
4128 bool shouldContinue
= false;
4129 if (strcmp(aTopic
, FullscreenTransitionTask::kPaintedTopic
) == 0) {
4130 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryInterface(aSubject
));
4131 nsCOMPtr
<nsIWidget
> widget
=
4132 win
? nsGlobalWindowInner::Cast(win
)->GetMainWidget() : nullptr;
4133 if (widget
== mTask
->mWidget
) {
4134 // The paint notification arrives first. Cancel the timer.
4135 mTask
->mTimer
->Cancel();
4136 shouldContinue
= true;
4137 PROFILER_MARKER_UNTYPED("Fullscreen toggle end", DOM
);
4141 MOZ_ASSERT(strcmp(aTopic
, NS_TIMER_CALLBACK_TOPIC
) == 0,
4142 "Should only get fullscreen-painted or timer-callback");
4143 nsCOMPtr
<nsITimer
> timer(do_QueryInterface(aSubject
));
4144 MOZ_ASSERT(timer
&& timer
== mTask
->mTimer
,
4145 "Should only trigger this with the timer the task created");
4147 shouldContinue
= true;
4148 PROFILER_MARKER_UNTYPED("Fullscreen toggle timeout", DOM
);
4150 if (shouldContinue
) {
4151 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
4152 obs
->RemoveObserver(this, kPaintedTopic
);
4153 mTask
->mTimer
= nullptr;
4160 FullscreenTransitionTask::Observer::GetName(nsACString
& aName
) {
4161 aName
.AssignLiteral("FullscreenTransitionTask");
4165 static bool MakeWidgetFullscreen(nsGlobalWindowOuter
* aWindow
,
4166 FullscreenReason aReason
, bool aFullscreen
) {
4167 nsCOMPtr
<nsIWidget
> widget
= aWindow
->GetMainWidget();
4172 FullscreenTransitionDuration duration
;
4173 bool performTransition
= false;
4174 nsCOMPtr
<nsISupports
> transitionData
;
4175 if (aReason
== FullscreenReason::ForFullscreenAPI
) {
4176 GetFullscreenTransitionDuration(aFullscreen
, &duration
);
4177 if (!duration
.IsSuppressed()) {
4178 performTransition
= widget
->PrepareForFullscreenTransition(
4179 getter_AddRefs(transitionData
));
4183 if (!performTransition
) {
4184 return aWindow
->SetWidgetFullscreen(aReason
, aFullscreen
, widget
);
4187 nsCOMPtr
<nsIRunnable
> task
= new FullscreenTransitionTask(
4188 duration
, aWindow
, aFullscreen
, widget
, transitionData
);
4193 nsresult
nsGlobalWindowOuter::ProcessWidgetFullscreenRequest(
4194 FullscreenReason aReason
, bool aFullscreen
) {
4195 mInProcessFullscreenRequest
.emplace(aReason
, aFullscreen
);
4197 // Prevent chrome documents which are still loading from resizing
4198 // the window after we set fullscreen mode.
4199 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
4200 nsCOMPtr
<nsIAppWindow
> appWin(do_GetInterface(treeOwnerAsWin
));
4201 if (aFullscreen
&& appWin
) {
4202 appWin
->SetIntrinsicallySized(false);
4205 // Sometimes we don't want the top-level widget to actually go fullscreen:
4206 // - in the B2G desktop client, we don't want the emulated screen dimensions
4207 // to appear to increase when entering fullscreen mode; we just want the
4208 // content to fill the entire client area of the emulator window.
4209 // - in FxR Desktop, we don't want fullscreen to take over the monitor, but
4210 // instead we want fullscreen to fill the FxR window in the the headset.
4211 if (!StaticPrefs::full_screen_api_ignore_widgets() &&
4212 !mForceFullScreenInWidget
) {
4213 if (MakeWidgetFullscreen(this, aReason
, aFullscreen
)) {
4214 // The rest of code for switching fullscreen is in nsGlobalWindowOuter::
4215 // FinishFullscreenChange() which will be called after sizemodechange
4216 // event is dispatched.
4221 #if defined(NIGHTLY_BUILD) && defined(XP_WIN)
4222 if (FxRWindowManager::GetInstance()->IsFxRWindow(mWindowID
)) {
4223 mozilla::gfx::VRShMem
shmem(nullptr, true /*aRequiresMutex*/);
4224 shmem
.SendFullscreenState(mWindowID
, aFullscreen
);
4226 #endif // NIGHTLY_BUILD && XP_WIN
4227 FinishFullscreenChange(aFullscreen
);
4231 nsresult
nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason
,
4233 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
4234 "Requires safe to run script as it "
4235 "may call FinishDOMFullscreenChange");
4237 NS_ENSURE_TRUE(mDocShell
, NS_ERROR_FAILURE
);
4240 aReason
!= FullscreenReason::ForForceExitFullscreen
|| !aFullscreen
,
4241 "FullscreenReason::ForForceExitFullscreen can "
4242 "only be used with exiting fullscreen");
4244 // Only chrome can change our fullscreen mode. Otherwise, the state
4245 // can only be changed for DOM fullscreen.
4246 if (aReason
== FullscreenReason::ForFullscreenMode
&&
4247 !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
4251 // SetFullscreen needs to be called on the root window, so get that
4252 // via the DocShell tree, and if we are not already the root,
4253 // call SetFullscreen on that window instead.
4254 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
4255 mDocShell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
4256 nsCOMPtr
<nsPIDOMWindowOuter
> window
=
4257 rootItem
? rootItem
->GetWindow() : nullptr;
4258 if (!window
) return NS_ERROR_FAILURE
;
4259 if (rootItem
!= mDocShell
)
4260 return window
->SetFullscreenInternal(aReason
, aFullscreen
);
4262 // make sure we don't try to set full screen on a non-chrome window,
4263 // which might happen in embedding world
4264 if (mDocShell
->ItemType() != nsIDocShellTreeItem::typeChrome
)
4265 return NS_ERROR_FAILURE
;
4267 // FullscreenReason::ForForceExitFullscreen can only be used with exiting
4270 mFullscreen
.isSome(),
4271 mFullscreen
.value() != FullscreenReason::ForForceExitFullscreen
);
4273 // If we are already in full screen mode, just return, we don't care about the
4274 // reason here, because,
4275 // - If we are in fullscreen mode due to browser fullscreen mode, requesting
4276 // DOM fullscreen does not change anything.
4277 // - If we are in fullscreen mode due to DOM fullscreen, requesting browser
4278 // fullscreen should not change anything, either. Note that we should not
4279 // update reason to ForFullscreenMode, otherwise the subsequent DOM
4280 // fullscreen exit will be ignored and user will be confused. And ideally
4281 // this should never happen as `window.fullscreen` returns `true` for DOM
4282 // fullscreen as well.
4283 if (mFullscreen
.isSome() == aFullscreen
) {
4284 // How come we get browser fullscreen request while we are already in DOM
4286 MOZ_ASSERT_IF(aFullscreen
&& aReason
== FullscreenReason::ForFullscreenMode
,
4287 mFullscreen
.value() != FullscreenReason::ForFullscreenAPI
);
4291 // Note that although entering DOM fullscreen could also cause
4292 // consequential calls to this method, those calls will be skipped
4293 // at the condition above.
4294 if (aReason
== FullscreenReason::ForFullscreenMode
) {
4295 if (!aFullscreen
&& mFullscreen
&&
4296 mFullscreen
.value() == FullscreenReason::ForFullscreenAPI
) {
4297 // If we are exiting fullscreen mode, but we actually didn't
4298 // entered browser fullscreen mode, the fullscreen state was only for
4299 // the Fullscreen API. Change the reason here so that we can
4300 // perform transition for it.
4301 aReason
= FullscreenReason::ForFullscreenAPI
;
4304 // If we are exiting from DOM fullscreen while we initially make
4305 // the window fullscreen because of browser fullscreen mode, don't restore
4306 // the window. But we still need to exit the DOM fullscreen state.
4307 if (!aFullscreen
&& mFullscreen
&&
4308 mFullscreen
.value() == FullscreenReason::ForFullscreenMode
) {
4309 // If there is a in-process fullscreen request, FinishDOMFullscreenChange
4310 // will be called when the request is finished.
4311 if (!mInProcessFullscreenRequest
.isSome()) {
4312 FinishDOMFullscreenChange(mDoc
, false);
4318 // Set this before so if widget sends an event indicating its
4319 // gone full screen, the state trap above works.
4321 mFullscreen
.emplace(aReason
);
4323 mFullscreen
.reset();
4326 // If we are in process of fullscreen request, only keep the latest fullscreen
4327 // state, we will sync up later while the processing request is finished.
4328 if (mInProcessFullscreenRequest
.isSome()) {
4329 mFullscreenHasChangedDuringProcessing
= true;
4333 return ProcessWidgetFullscreenRequest(aReason
, aFullscreen
);
4336 // Support a per-window, dynamic equivalent of enabling
4337 // full-screen-api.ignore-widgets
4338 void nsGlobalWindowOuter::ForceFullScreenInWidget() {
4339 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
4341 mForceFullScreenInWidget
= true;
4344 bool nsGlobalWindowOuter::SetWidgetFullscreen(FullscreenReason aReason
,
4346 nsIWidget
* aWidget
) {
4347 MOZ_ASSERT(this == GetInProcessTopInternal(),
4348 "Only topmost window should call this");
4349 MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this");
4350 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default
);
4352 if (!NS_WARN_IF(!IsChromeWindow())) {
4353 if (!NS_WARN_IF(mChromeFields
.mFullscreenPresShell
)) {
4354 if (PresShell
* presShell
= mDocShell
->GetPresShell()) {
4355 if (nsRefreshDriver
* rd
= presShell
->GetRefreshDriver()) {
4356 mChromeFields
.mFullscreenPresShell
= do_GetWeakReference(presShell
);
4357 MOZ_ASSERT(mChromeFields
.mFullscreenPresShell
);
4358 rd
->SetIsResizeSuppressed();
4364 nsresult rv
= aReason
== FullscreenReason::ForFullscreenMode
4366 // If we enter fullscreen for fullscreen mode, we want
4367 // the native system behavior.
4368 aWidget
->MakeFullScreenWithNativeTransition(aIsFullscreen
)
4369 : aWidget
->MakeFullScreen(aIsFullscreen
);
4370 return NS_SUCCEEDED(rv
);
4374 void nsGlobalWindowOuter::FullscreenWillChange(bool aIsFullscreen
) {
4375 if (!mInProcessFullscreenRequest
.isSome()) {
4376 // If there is no in-process fullscreen request, the fullscreen state change
4377 // is triggered from the OS directly, e.g. user use built-in window button
4378 // to enter/exit fullscreen on macOS.
4379 mInProcessFullscreenRequest
.emplace(FullscreenReason::ForFullscreenMode
,
4381 if (mFullscreen
.isSome() != aIsFullscreen
) {
4382 if (aIsFullscreen
) {
4383 mFullscreen
.emplace(FullscreenReason::ForFullscreenMode
);
4385 mFullscreen
.reset();
4388 // It is possible that FullscreenWillChange is notified with current
4389 // fullscreen state, e.g. browser goes into fullscreen when widget
4390 // fullscreen is prevented, and then user triggers fullscreen from the OS
4392 MOZ_ASSERT(StaticPrefs::full_screen_api_ignore_widgets() ||
4393 mForceFullScreenInWidget
,
4394 "This should only happen when widget fullscreen is prevented");
4397 if (aIsFullscreen
) {
4398 DispatchCustomEvent(u
"willenterfullscreen"_ns
, ChromeOnlyDispatch::eYes
);
4400 DispatchCustomEvent(u
"willexitfullscreen"_ns
, ChromeOnlyDispatch::eYes
);
4405 void nsGlobalWindowOuter::FinishFullscreenChange(bool aIsFullscreen
) {
4406 mozilla::Maybe
<FullscreenRequest
> currentInProcessRequest
=
4407 std::move(mInProcessFullscreenRequest
);
4408 if (!mFullscreenHasChangedDuringProcessing
&&
4409 aIsFullscreen
!= mFullscreen
.isSome()) {
4410 NS_WARNING("Failed to toggle fullscreen state of the widget");
4411 // We failed to make the widget enter fullscreen.
4412 // Stop further changes and restore the state.
4413 if (!aIsFullscreen
) {
4414 mFullscreen
.reset();
4417 MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?");
4419 // Restore fullscreen state with FullscreenReason::ForFullscreenAPI reason
4420 // in order to make subsequent DOM fullscreen exit request can exit
4421 // browser fullscreen mode.
4422 mFullscreen
.emplace(FullscreenReason::ForFullscreenAPI
);
4427 // Note that we must call this to toggle the DOM fullscreen state
4428 // of the document before dispatching the "fullscreen" event, so
4429 // that the chrome can distinguish between browser fullscreen mode
4430 // and DOM fullscreen.
4431 FinishDOMFullscreenChange(mDoc
, aIsFullscreen
);
4433 // dispatch a "fullscreen" DOM event so that XUL apps can
4434 // respond visually if we are kicked into full screen mode
4435 DispatchCustomEvent(u
"fullscreen"_ns
, ChromeOnlyDispatch::eYes
);
4437 if (!NS_WARN_IF(!IsChromeWindow())) {
4438 if (RefPtr
<PresShell
> presShell
=
4439 do_QueryReferent(mChromeFields
.mFullscreenPresShell
)) {
4440 if (nsRefreshDriver
* rd
= presShell
->GetRefreshDriver()) {
4443 mChromeFields
.mFullscreenPresShell
= nullptr;
4447 // If fullscreen state has changed during processing fullscreen request, we
4448 // need to ensure widget matches our latest fullscreen state here.
4449 if (mFullscreenHasChangedDuringProcessing
) {
4450 mFullscreenHasChangedDuringProcessing
= false;
4451 // Widget doesn't care about the reason that makes it entering/exiting
4452 // fullscreen, so here we just need to ensure the fullscreen state is
4454 if (aIsFullscreen
!= mFullscreen
.isSome()) {
4455 // If we end up need to exit fullscreen, use the same reason that brings
4456 // us into fullscreen mode, so that we will perform the same fullscreen
4457 // transistion effect for exiting.
4458 ProcessWidgetFullscreenRequest(
4459 mFullscreen
.isSome() ? mFullscreen
.value()
4460 : currentInProcessRequest
.value().mReason
,
4461 mFullscreen
.isSome());
4467 void nsGlobalWindowOuter::MacFullscreenMenubarOverlapChanged(
4468 mozilla::DesktopCoord aOverlapAmount
) {
4470 RefPtr
<Event
> domEvent
=
4471 mDoc
->CreateEvent(u
"CustomEvent"_ns
, CallerType::System
, res
);
4478 JSContext
* cx
= jsapi
.cx();
4479 JSAutoRealm
ar(cx
, GetWrapperPreserveColor());
4481 JS::Rooted
<JS::Value
> detailValue(cx
);
4482 if (!ToJSValue(cx
, aOverlapAmount
, &detailValue
)) {
4486 CustomEvent
* customEvent
= static_cast<CustomEvent
*>(domEvent
.get());
4487 customEvent
->InitCustomEvent(cx
, u
"MacFullscreenMenubarRevealUpdate"_ns
,
4488 /* aCanBubble = */ true,
4489 /* aCancelable = */ true, detailValue
);
4490 domEvent
->SetTrusted(true);
4491 domEvent
->WidgetEventPtr()->mFlags
.mOnlyChromeDispatch
= true;
4493 nsCOMPtr
<EventTarget
> target
= this;
4494 domEvent
->SetTarget(target
);
4496 target
->DispatchEvent(*domEvent
, CallerType::System
, IgnoreErrors());
4499 bool nsGlobalWindowOuter::Fullscreen() const {
4500 NS_ENSURE_TRUE(mDocShell
, mFullscreen
.isSome());
4502 // Get the fullscreen value of the root window, to always have the value
4503 // accurate, even when called from content.
4504 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
4505 mDocShell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
4506 if (rootItem
== mDocShell
) {
4507 if (!XRE_IsContentProcess()) {
4508 // We are the root window. Return our internal value.
4509 return mFullscreen
.isSome();
4511 if (nsCOMPtr
<nsIWidget
> widget
= GetNearestWidget()) {
4512 // We are in content process, figure out the value from
4513 // the sizemode of the puppet widget.
4514 return widget
->SizeMode() == nsSizeMode_Fullscreen
;
4519 nsCOMPtr
<nsPIDOMWindowOuter
> window
= rootItem
->GetWindow();
4520 NS_ENSURE_TRUE(window
, mFullscreen
.isSome());
4522 return nsGlobalWindowOuter::Cast(window
)->Fullscreen();
4525 bool nsGlobalWindowOuter::GetFullscreenOuter() { return Fullscreen(); }
4527 bool nsGlobalWindowOuter::GetFullScreen() {
4528 FORWARD_TO_INNER(GetFullScreen
, (), false);
4531 void nsGlobalWindowOuter::EnsureReflowFlushAndPaint() {
4532 NS_ASSERTION(mDocShell
,
4533 "EnsureReflowFlushAndPaint() called with no "
4536 if (!mDocShell
) return;
4538 RefPtr
<PresShell
> presShell
= mDocShell
->GetPresShell();
4543 // Flush pending reflows.
4545 mDoc
->FlushPendingNotifications(FlushType::Layout
);
4548 // Unsuppress painting.
4549 presShell
->UnsuppressPainting();
4553 void nsGlobalWindowOuter::MakeMessageWithPrincipal(
4554 nsAString
& aOutMessage
, nsIPrincipal
* aSubjectPrincipal
, bool aUseHostPort
,
4555 const char* aNullMessage
, const char* aContentMessage
,
4556 const char* aFallbackMessage
) {
4557 MOZ_ASSERT(aSubjectPrincipal
);
4559 aOutMessage
.Truncate();
4561 // Try to get a host from the running principal -- this will do the
4562 // right thing for javascript: and data: documents.
4564 nsAutoCString contentDesc
;
4566 if (aSubjectPrincipal
->GetIsNullPrincipal()) {
4567 nsContentUtils::GetLocalizedString(
4568 nsContentUtils::eCOMMON_DIALOG_PROPERTIES
, aNullMessage
, aOutMessage
);
4570 auto* addonPolicy
= BasePrincipal::Cast(aSubjectPrincipal
)->AddonPolicy();
4572 nsContentUtils::FormatLocalizedString(
4573 aOutMessage
, nsContentUtils::eCOMMON_DIALOG_PROPERTIES
,
4574 aContentMessage
, addonPolicy
->Name());
4576 nsresult rv
= NS_ERROR_FAILURE
;
4578 nsCOMPtr
<nsIURI
> uri
= aSubjectPrincipal
->GetURI();
4580 rv
= uri
->GetDisplayHostPort(contentDesc
);
4583 if (!aUseHostPort
|| NS_FAILED(rv
)) {
4584 rv
= aSubjectPrincipal
->GetExposablePrePath(contentDesc
);
4586 if (NS_SUCCEEDED(rv
) && !contentDesc
.IsEmpty()) {
4587 NS_ConvertUTF8toUTF16
ucsPrePath(contentDesc
);
4588 nsContentUtils::FormatLocalizedString(
4589 aOutMessage
, nsContentUtils::eCOMMON_DIALOG_PROPERTIES
,
4590 aContentMessage
, ucsPrePath
);
4595 if (aOutMessage
.IsEmpty()) {
4596 // We didn't find a host so use the generic heading
4597 nsContentUtils::GetLocalizedString(
4598 nsContentUtils::eCOMMON_DIALOG_PROPERTIES
, aFallbackMessage
,
4603 if (aOutMessage
.IsEmpty()) {
4605 "could not get ScriptDlgGenericHeading string from string bundle");
4606 aOutMessage
.AssignLiteral("[Script]");
4610 bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType
) {
4611 // When called from chrome, we can avoid the following checks.
4612 if (aCallerType
!= CallerType::System
) {
4613 // Don't allow scripts to move or resize windows that were not opened by a
4615 if (!mBrowsingContext
->GetTopLevelCreatedByWebContent()) {
4619 if (!CanSetProperty("dom.disable_window_move_resize")) {
4623 // Ignore the request if we have more than one tab in the window.
4624 if (mBrowsingContext
->Top()->HasSiblings()) {
4631 nsresult rv
= mDocShell
->GetAllowWindowControl(&allow
);
4632 if (NS_SUCCEEDED(rv
) && !allow
) return false;
4635 if (nsGlobalWindowInner::sMouseDown
&&
4636 !nsGlobalWindowInner::sDragServiceDisabled
) {
4637 nsCOMPtr
<nsIDragService
> ds
=
4638 do_GetService("@mozilla.org/widget/dragservice;1");
4640 nsGlobalWindowInner::sDragServiceDisabled
= true;
4647 bool nsGlobalWindowOuter::AlertOrConfirm(bool aAlert
, const nsAString
& aMessage
,
4648 nsIPrincipal
& aSubjectPrincipal
,
4649 ErrorResult
& aError
) {
4650 // XXX This method is very similar to nsGlobalWindowOuter::Prompt, make
4651 // sure any modifications here don't need to happen over there!
4652 if (!AreDialogsEnabled()) {
4653 // Just silently return. In the case of alert(), the return value is
4654 // ignored. In the case of confirm(), returning false is the same thing as
4655 // would happen if the user cancels.
4659 // Reset popup state while opening a modal dialog, and firing events
4660 // about the dialog, to prevent the current state from being active
4661 // the whole time a modal dialog is open.
4662 AutoPopupStatePusher
popupStatePusher(PopupBlocker::openAbused
, true);
4664 // Before bringing up the window, unsuppress painting and flush
4666 EnsureReflowFlushAndPaint();
4669 MakeMessageWithPrincipal(title
, &aSubjectPrincipal
, false,
4670 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
4671 "ScriptDlgGenericHeading");
4673 // Remove non-terminating null characters from the
4674 // string. See bug #310037.
4676 nsContentUtils::StripNullChars(aMessage
, final
);
4677 nsContentUtils::PlatformToDOMLineBreaks(final
);
4680 nsCOMPtr
<nsIPromptFactory
> promptFac
=
4681 do_GetService("@mozilla.org/prompter;1", &rv
);
4682 if (NS_FAILED(rv
)) {
4687 nsCOMPtr
<nsIPrompt
> prompt
;
4689 promptFac
->GetPrompt(this, NS_GET_IID(nsIPrompt
), getter_AddRefs(prompt
));
4690 if (aError
.Failed()) {
4694 // Always allow content modal prompts for alert and confirm.
4695 if (nsCOMPtr
<nsIWritablePropertyBag2
> promptBag
= do_QueryInterface(prompt
)) {
4696 promptBag
->SetPropertyAsUint32(u
"modalType"_ns
,
4697 nsIPrompt::MODAL_TYPE_CONTENT
);
4700 bool result
= false;
4701 nsAutoSyncOperation
sync(mDoc
, SyncOperationBehavior::eSuspendInput
);
4702 if (ShouldPromptToBlockDialogs()) {
4703 bool disallowDialog
= false;
4705 MakeMessageWithPrincipal(
4706 label
, &aSubjectPrincipal
, true, "ScriptDialogLabelNullPrincipal",
4707 "ScriptDialogLabelContentPrincipal", "ScriptDialogLabelNullPrincipal");
4710 ? prompt
->AlertCheck(title
.get(), final
.get(), label
.get(),
4712 : prompt
->ConfirmCheck(title
.get(), final
.get(), label
.get(),
4713 &disallowDialog
, &result
);
4715 if (disallowDialog
) {
4719 aError
= aAlert
? prompt
->Alert(title
.get(), final
.get())
4720 : prompt
->Confirm(title
.get(), final
.get(), &result
);
4726 void nsGlobalWindowOuter::AlertOuter(const nsAString
& aMessage
,
4727 nsIPrincipal
& aSubjectPrincipal
,
4728 ErrorResult
& aError
) {
4729 AlertOrConfirm(/* aAlert = */ true, aMessage
, aSubjectPrincipal
, aError
);
4732 bool nsGlobalWindowOuter::ConfirmOuter(const nsAString
& aMessage
,
4733 nsIPrincipal
& aSubjectPrincipal
,
4734 ErrorResult
& aError
) {
4735 return AlertOrConfirm(/* aAlert = */ false, aMessage
, aSubjectPrincipal
,
4739 void nsGlobalWindowOuter::PromptOuter(const nsAString
& aMessage
,
4740 const nsAString
& aInitial
,
4742 nsIPrincipal
& aSubjectPrincipal
,
4743 ErrorResult
& aError
) {
4744 // XXX This method is very similar to nsGlobalWindowOuter::AlertOrConfirm,
4745 // make sure any modifications here don't need to happen over there!
4746 SetDOMStringToNull(aReturn
);
4748 if (!AreDialogsEnabled()) {
4749 // Return null, as if the user just canceled the prompt.
4753 // Reset popup state while opening a modal dialog, and firing events
4754 // about the dialog, to prevent the current state from being active
4755 // the whole time a modal dialog is open.
4756 AutoPopupStatePusher
popupStatePusher(PopupBlocker::openAbused
, true);
4758 // Before bringing up the window, unsuppress painting and flush
4760 EnsureReflowFlushAndPaint();
4763 MakeMessageWithPrincipal(title
, &aSubjectPrincipal
, false,
4764 "ScriptDlgNullPrincipalHeading", "ScriptDlgHeading",
4765 "ScriptDlgGenericHeading");
4767 // Remove non-terminating null characters from the
4768 // string. See bug #310037.
4769 nsAutoString fixedMessage
, fixedInitial
;
4770 nsContentUtils::StripNullChars(aMessage
, fixedMessage
);
4771 nsContentUtils::PlatformToDOMLineBreaks(fixedMessage
);
4772 nsContentUtils::StripNullChars(aInitial
, fixedInitial
);
4775 nsCOMPtr
<nsIPromptFactory
> promptFac
=
4776 do_GetService("@mozilla.org/prompter;1", &rv
);
4777 if (NS_FAILED(rv
)) {
4782 nsCOMPtr
<nsIPrompt
> prompt
;
4784 promptFac
->GetPrompt(this, NS_GET_IID(nsIPrompt
), getter_AddRefs(prompt
));
4785 if (aError
.Failed()) {
4789 // Always allow content modal prompts for prompt.
4790 if (nsCOMPtr
<nsIWritablePropertyBag2
> promptBag
= do_QueryInterface(prompt
)) {
4791 promptBag
->SetPropertyAsUint32(u
"modalType"_ns
,
4792 nsIPrompt::MODAL_TYPE_CONTENT
);
4795 // Pass in the default value, if any.
4796 char16_t
* inoutValue
= ToNewUnicode(fixedInitial
);
4797 bool disallowDialog
= false;
4800 label
.SetIsVoid(true);
4801 if (ShouldPromptToBlockDialogs()) {
4802 nsContentUtils::GetLocalizedString(
4803 nsContentUtils::eCOMMON_DIALOG_PROPERTIES
, "ScriptDialogLabel", label
);
4806 nsAutoSyncOperation
sync(mDoc
, SyncOperationBehavior::eSuspendInput
);
4808 aError
= prompt
->Prompt(title
.get(), fixedMessage
.get(), &inoutValue
,
4809 label
.IsVoid() ? nullptr : label
.get(),
4810 &disallowDialog
, &ok
);
4812 if (disallowDialog
) {
4816 // XXX Doesn't this leak inoutValue?
4817 if (aError
.Failed()) {
4822 outValue
.Adopt(inoutValue
);
4823 if (ok
&& inoutValue
) {
4824 aReturn
= std::move(outValue
);
4828 void nsGlobalWindowOuter::FocusOuter(CallerType aCallerType
,
4829 bool aFromOtherProcess
,
4830 uint64_t aActionId
) {
4831 RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager();
4832 if (MOZ_UNLIKELY(!fm
)) {
4836 auto [canFocus
, isActive
] = GetBrowsingContext()->CanFocusCheck(aCallerType
);
4837 if (aFromOtherProcess
) {
4838 // We trust that the check passed in a process that's, in principle,
4839 // untrusted, because we don't have the required caller context available
4840 // here. Also, the worst that the other process can do in this case is to
4841 // raise a window it's not supposed to be allowed to raise.
4842 // https://bugzilla.mozilla.org/show_bug.cgi?id=1677899
4843 MOZ_ASSERT(XRE_IsContentProcess(),
4844 "Parent should not trust other processes.");
4848 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
4849 if (treeOwnerAsWin
&& (canFocus
|| isActive
)) {
4850 bool isEnabled
= true;
4851 if (NS_SUCCEEDED(treeOwnerAsWin
->GetEnabled(&isEnabled
)) && !isEnabled
) {
4852 NS_WARNING("Should not try to set the focus on a disabled window");
4861 // If the window has a child frame focused, clear the focus. This
4862 // ensures that focus will be in this frame and not in a child.
4863 if (nsIContent
* content
= GetFocusedElement()) {
4864 if (HTMLIFrameElement::FromNode(content
)) {
4865 fm
->ClearFocus(this);
4869 RefPtr
<BrowsingContext
> parent
;
4870 BrowsingContext
* bc
= GetBrowsingContext();
4872 parent
= bc
->GetParent();
4873 if (!parent
&& XRE_IsParentProcess()) {
4874 parent
= bc
->Canonical()->GetParentCrossChromeBoundary();
4878 if (!parent
->IsInProcess()) {
4880 OwningNonNull
<nsGlobalWindowOuter
> kungFuDeathGrip(*this);
4881 fm
->WindowRaised(kungFuDeathGrip
, aActionId
);
4883 ContentChild
* contentChild
= ContentChild::GetSingleton();
4884 MOZ_ASSERT(contentChild
);
4885 contentChild
->SendFinalizeFocusOuter(bc
, canFocus
, aCallerType
);
4890 MOZ_ASSERT(mDoc
, "Call chain should have ensured document creation.");
4892 if (Element
* frame
= mDoc
->GetEmbedderElement()) {
4893 nsContentUtils::RequestFrameFocus(*frame
, canFocus
, aCallerType
);
4900 // if there is no parent, this must be a toplevel window, so raise the
4901 // window if canFocus is true. If this is a child process, the raise
4902 // window request will get forwarded to the parent by the puppet widget.
4903 OwningNonNull
<nsGlobalWindowOuter
> kungFuDeathGrip(*this);
4904 fm
->RaiseWindow(kungFuDeathGrip
, aCallerType
, aActionId
);
4908 nsresult
nsGlobalWindowOuter::Focus(CallerType aCallerType
) {
4909 FORWARD_TO_INNER(Focus
, (aCallerType
), NS_ERROR_UNEXPECTED
);
4912 void nsGlobalWindowOuter::BlurOuter(CallerType aCallerType
) {
4913 if (!GetBrowsingContext()->CanBlurCheck(aCallerType
)) {
4917 nsCOMPtr
<nsIWebBrowserChrome
> chrome
= GetWebBrowserChrome();
4923 void nsGlobalWindowOuter::StopOuter(ErrorResult
& aError
) {
4924 // IsNavigationAllowed checks are usually done in nsDocShell directly,
4925 // however nsDocShell::Stop has a bunch of internal users that would fail
4926 // the IsNavigationAllowed check.
4927 if (!mDocShell
|| !nsDocShell::Cast(mDocShell
)->IsNavigationAllowed()) {
4931 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(mDocShell
));
4933 aError
= webNav
->Stop(nsIWebNavigation::STOP_ALL
);
4937 void nsGlobalWindowOuter::PrintOuter(ErrorResult
& aError
) {
4938 if (!AreDialogsEnabled()) {
4939 // Per spec, silently return. https://github.com/whatwg/html/commit/21a1de1
4943 // Printing is disabled, silently return.
4944 if (!StaticPrefs::print_enabled()) {
4948 // If we're loading, queue the print for later. This is a special-case that
4949 // only applies to the window.print() call, for compat with other engines and
4950 // pre-existing behavior.
4951 if (mShouldDelayPrintUntilAfterLoad
) {
4952 if (nsIDocShell
* docShell
= GetDocShell()) {
4953 if (docShell
->GetBusyFlags() & nsIDocShell::BUSY_FLAGS_PAGE_LOADING
) {
4954 mDelayedPrintUntilAfterLoad
= true;
4961 RefPtr
<BrowsingContext
> top
=
4962 mBrowsingContext
? mBrowsingContext
->Top() : nullptr;
4963 if (NS_WARN_IF(top
&& top
->GetIsPrinting())) {
4968 Unused
<< top
->SetIsPrinting(true);
4971 auto unset
= MakeScopeExit([&] {
4973 Unused
<< top
->SetIsPrinting(false);
4977 const bool forPreview
=
4978 !StaticPrefs::print_always_print_silent() &&
4979 !Preferences::GetBool("print.prefer_system_dialog", false);
4980 Print(nullptr, nullptr, nullptr, nullptr, IsPreview(forPreview
),
4981 IsForWindowDotPrint::Yes
, nullptr, nullptr, aError
);
4985 class MOZ_RAII AutoModalState
{
4987 explicit AutoModalState(nsGlobalWindowOuter
& aWin
)
4988 : mModalStateWin(aWin
.EnterModalState()) {}
4991 if (mModalStateWin
) {
4992 mModalStateWin
->LeaveModalState();
4996 RefPtr
<nsGlobalWindowOuter
> mModalStateWin
;
4999 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::Print(
5000 nsIPrintSettings
* aPrintSettings
, RemotePrintJobChild
* aRemotePrintJob
,
5001 nsIWebProgressListener
* aListener
, nsIDocShell
* aDocShellToCloneInto
,
5002 IsPreview aIsPreview
, IsForWindowDotPrint aForWindowDotPrint
,
5003 PrintPreviewResolver
&& aPrintPreviewCallback
,
5004 RefPtr
<BrowsingContext
>* aCachedBrowsingContext
, ErrorResult
& aError
) {
5006 nsCOMPtr
<nsIPrintSettingsService
> printSettingsService
=
5007 do_GetService("@mozilla.org/gfx/printsettings-service;1");
5008 if (!printSettingsService
) {
5009 // we currently return here in headless mode - should we?
5010 aError
.ThrowNotSupportedError("No print settings service");
5014 nsCOMPtr
<nsIPrintSettings
> ps
= aPrintSettings
;
5016 // We shouldn't need this once bug 1776169 is fixed.
5017 printSettingsService
->GetDefaultPrintSettingsForPrinting(
5018 getter_AddRefs(ps
));
5021 RefPtr
<Document
> docToPrint
= mDoc
;
5022 if (NS_WARN_IF(!docToPrint
)) {
5023 aError
.ThrowNotSupportedError("Document is gone");
5027 RefPtr
<BrowsingContext
> sourceBC
= docToPrint
->GetBrowsingContext();
5028 MOZ_DIAGNOSTIC_ASSERT(sourceBC
);
5030 aError
.ThrowNotSupportedError("No browsing context for source document");
5034 nsAutoSyncOperation
sync(docToPrint
, SyncOperationBehavior::eAllowInput
);
5035 AutoModalState
modalState(*this);
5037 nsCOMPtr
<nsIDocumentViewer
> viewer
;
5038 RefPtr
<BrowsingContext
> bc
;
5039 bool hasPrintCallbacks
= false;
5040 bool wasStaticDocument
= docToPrint
->IsStaticDocument();
5041 bool usingCachedBrowsingContext
= false;
5042 if (aCachedBrowsingContext
&& *aCachedBrowsingContext
) {
5043 MOZ_ASSERT(!wasStaticDocument
,
5044 "Why pass in non-empty aCachedBrowsingContext if original "
5045 "document is already static?");
5046 if (!wasStaticDocument
) {
5047 // The passed in document is not a static clone and the caller passed in a
5048 // static clone to reuse, so swap it in.
5049 docToPrint
= (*aCachedBrowsingContext
)->GetDocument();
5050 MOZ_ASSERT(docToPrint
);
5051 MOZ_ASSERT(docToPrint
->IsStaticDocument());
5052 wasStaticDocument
= true;
5053 usingCachedBrowsingContext
= true;
5056 if (wasStaticDocument
) {
5057 if (aForWindowDotPrint
== IsForWindowDotPrint::Yes
) {
5058 aError
.ThrowNotSupportedError(
5059 "Calling print() from a print preview is unsupported, did you intend "
5060 "to call printPreview() instead?");
5063 if (usingCachedBrowsingContext
) {
5064 bc
= docToPrint
->GetBrowsingContext();
5066 // We're already a print preview window, just reuse our browsing context /
5070 nsCOMPtr
<nsIDocShell
> docShell
= bc
->GetDocShell();
5072 aError
.ThrowNotSupportedError("No docshell");
5075 // We could handle this if needed.
5076 if (aDocShellToCloneInto
&& aDocShellToCloneInto
!= docShell
) {
5077 aError
.ThrowNotSupportedError(
5078 "We don't handle cloning a print preview doc into a different "
5082 docShell
->GetDocViewer(getter_AddRefs(viewer
));
5083 MOZ_DIAGNOSTIC_ASSERT(viewer
);
5085 if (aDocShellToCloneInto
) {
5086 // Ensure the content viewer is created if needed.
5087 Unused
<< aDocShellToCloneInto
->GetDocument();
5088 bc
= aDocShellToCloneInto
->GetBrowsingContext();
5090 AutoNoJSAPI nojsapi
;
5091 auto printKind
= aForWindowDotPrint
== IsForWindowDotPrint::Yes
5092 ? PrintKind::WindowDotPrint
5093 : PrintKind::InternalPrint
;
5094 // For PrintKind::WindowDotPrint, this call will not only make the parent
5095 // process create a CanonicalBrowsingContext for the returned `bc`, but
5096 // it will also make the parent process initiate the print/print preview.
5097 // See the handling of OPEN_PRINT_BROWSER in browser.js.
5098 aError
= OpenInternal(""_ns
, u
""_ns
, u
""_ns
,
5100 true, // aCalledNoScript
5101 false, // aDoJSFixups
5104 nullptr, // aLoadState
5105 false, // aForceNoOpener
5106 printKind
, getter_AddRefs(bc
));
5107 if (NS_WARN_IF(aError
.Failed())) {
5110 if (aCachedBrowsingContext
) {
5111 MOZ_ASSERT(!*aCachedBrowsingContext
);
5112 *aCachedBrowsingContext
= bc
;
5116 aError
.ThrowNotAllowedError("No browsing context");
5120 Unused
<< bc
->Top()->SetIsPrinting(true);
5121 nsCOMPtr
<nsIDocShell
> cloneDocShell
= bc
->GetDocShell();
5122 MOZ_DIAGNOSTIC_ASSERT(cloneDocShell
);
5123 cloneDocShell
->GetDocViewer(getter_AddRefs(viewer
));
5124 MOZ_DIAGNOSTIC_ASSERT(viewer
);
5126 aError
.ThrowNotSupportedError("Didn't end up with a content viewer");
5130 if (bc
!= sourceBC
) {
5131 MOZ_ASSERT(bc
->IsTopContent());
5132 // If we are cloning from a document in a different BrowsingContext, we
5133 // need to make sure to copy over our opener policy information from that
5134 // BrowsingContext. In the case where the source is an iframe, this
5135 // information needs to be copied from the toplevel source
5136 // BrowsingContext, as we may be making a static clone of a single
5138 MOZ_ALWAYS_SUCCEEDS(
5139 bc
->SetOpenerPolicy(sourceBC
->Top()->GetOpenerPolicy()));
5140 MOZ_DIAGNOSTIC_ASSERT(bc
->Group() == sourceBC
->Group());
5143 if (RefPtr
<Document
> doc
= viewer
->GetDocument()) {
5144 if (doc
->IsShowing()) {
5145 // We're going to drop this document on the floor, in the SetDocument
5146 // call below. Make sure to run OnPageHide() to keep state consistent
5147 // and avoids assertions in the document destructor.
5148 doc
->OnPageHide(false, nullptr);
5152 AutoPrintEventDispatcher
dispatcher(*docToPrint
);
5154 nsAutoScriptBlocker blockScripts
;
5155 RefPtr
<Document
> clone
= docToPrint
->CreateStaticClone(
5156 cloneDocShell
, viewer
, ps
, &hasPrintCallbacks
);
5158 aError
.ThrowNotSupportedError("Clone operation for printing failed");
5163 nsCOMPtr
<nsIWebBrowserPrint
> webBrowserPrint
= do_QueryInterface(viewer
);
5164 if (!webBrowserPrint
) {
5165 aError
.ThrowNotSupportedError(
5166 "Content viewer didn't implement nsIWebBrowserPrint");
5169 bool closeWindowAfterPrint
;
5170 if (wasStaticDocument
) {
5171 // Here the document was a static clone to begin with that this code did not
5172 // create, so we should not clean it up.
5173 // The exception is if we're using the passed-in aCachedBrowsingContext, in
5174 // which case this is the second print with this static document clone that
5175 // we created the first time through, and we are responsible for cleaning it
5177 closeWindowAfterPrint
= usingCachedBrowsingContext
;
5179 // In this case the document was not a static clone, so we made a static
5180 // clone for printing purposes and must clean it up after the print is done.
5181 // The exception is if aCachedBrowsingContext is non-NULL, meaning the
5182 // caller is intending to print this document again, so we need to defer the
5183 // cleanup until after the second print.
5184 closeWindowAfterPrint
= !aCachedBrowsingContext
;
5186 webBrowserPrint
->SetCloseWindowAfterPrint(closeWindowAfterPrint
);
5188 // For window.print(), we postpone making these calls until the round-trip to
5189 // the parent process (triggered by the OpenInternal call above) calls us
5190 // again. Only a call from the parent can provide a valid nsPrintSettings
5191 // object and RemotePrintJobChild object.
5192 if (aForWindowDotPrint
== IsForWindowDotPrint::No
) {
5193 if (aIsPreview
== IsPreview::Yes
) {
5194 aError
= webBrowserPrint
->PrintPreview(ps
, aListener
,
5195 std::move(aPrintPreviewCallback
));
5196 if (aError
.Failed()) {
5200 // Historically we've eaten this error.
5201 webBrowserPrint
->Print(ps
, aRemotePrintJob
, aListener
);
5205 // When using window.print() with the new UI, we usually want to block until
5206 // the print dialog is hidden. But we can't really do that if we have print
5207 // callbacks, because we are inside a sync operation, and we want to run
5208 // microtasks / etc that the print callbacks may create. It is really awkward
5209 // to have this subtle behavior difference...
5211 // We also want to do this for fuzzing, so that they can test window.print().
5212 const bool shouldBlock
= [&] {
5213 if (aForWindowDotPrint
== IsForWindowDotPrint::No
) {
5216 if (aIsPreview
== IsPreview::Yes
) {
5217 return !hasPrintCallbacks
;
5219 return StaticPrefs::dom_window_print_fuzzing_block_while_printing();
5223 SpinEventLoopUntil("nsGlobalWindowOuter::Print"_ns
,
5224 [&] { return bc
->IsDiscarded(); });
5227 return WindowProxyHolder(std::move(bc
));
5230 #endif // NS_PRINTING
5233 void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos
, int32_t aYPos
,
5234 CallerType aCallerType
,
5235 ErrorResult
& aError
) {
5237 * If caller is not chrome and the user has not explicitly exempted the site,
5238 * prevent window.moveTo() by exiting early
5241 if (!CanMoveResizeWindows(aCallerType
) || mBrowsingContext
->IsSubframe()) {
5245 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5246 if (!treeOwnerAsWin
) {
5247 aError
.Throw(NS_ERROR_FAILURE
);
5251 // We need to do the same transformation GetScreenXY does.
5252 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
5257 CSSIntPoint
cssPos(aXPos
, aYPos
);
5258 CheckSecurityLeftAndTop(&cssPos
.x
.value
, &cssPos
.y
.value
, aCallerType
);
5260 nsDeviceContext
* context
= presContext
->DeviceContext();
5262 auto devPos
= LayoutDeviceIntPoint::FromAppUnitsRounded(
5263 CSSIntPoint::ToAppUnits(cssPos
), context
->AppUnitsPerDevPixel());
5265 aError
= treeOwnerAsWin
->SetPosition(devPos
.x
, devPos
.y
);
5266 CheckForDPIChange();
5269 void nsGlobalWindowOuter::MoveByOuter(int32_t aXDif
, int32_t aYDif
,
5270 CallerType aCallerType
,
5271 ErrorResult
& aError
) {
5273 * If caller is not chrome and the user has not explicitly exempted the site,
5274 * prevent window.moveBy() by exiting early
5277 if (!CanMoveResizeWindows(aCallerType
) || mBrowsingContext
->IsSubframe()) {
5281 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5282 if (!treeOwnerAsWin
) {
5283 aError
.Throw(NS_ERROR_FAILURE
);
5287 // To do this correctly we have to convert what we get from GetPosition
5288 // into CSS pixels, add the arguments, do the security check, and
5289 // then convert back to device pixels for the call to SetPosition.
5292 aError
= treeOwnerAsWin
->GetPosition(&x
, &y
);
5293 if (aError
.Failed()) {
5297 auto cssScale
= CSSToDevScaleForBaseWindow(treeOwnerAsWin
);
5298 CSSIntPoint cssPos
= RoundedToInt(treeOwnerAsWin
->GetPosition() / cssScale
);
5303 CheckSecurityLeftAndTop(&cssPos
.x
.value
, &cssPos
.y
.value
, aCallerType
);
5305 LayoutDeviceIntPoint newDevPos
= RoundedToInt(cssPos
* cssScale
);
5306 aError
= treeOwnerAsWin
->SetPosition(newDevPos
.x
, newDevPos
.y
);
5308 CheckForDPIChange();
5311 nsresult
nsGlobalWindowOuter::MoveBy(int32_t aXDif
, int32_t aYDif
) {
5313 MoveByOuter(aXDif
, aYDif
, CallerType::System
, rv
);
5315 return rv
.StealNSResult();
5318 void nsGlobalWindowOuter::ResizeToOuter(int32_t aWidth
, int32_t aHeight
,
5319 CallerType aCallerType
,
5320 ErrorResult
& aError
) {
5322 * If caller is not chrome and the user has not explicitly exempted the site,
5323 * prevent window.resizeTo() by exiting early
5326 if (!CanMoveResizeWindows(aCallerType
) || mBrowsingContext
->IsSubframe()) {
5330 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5331 if (!treeOwnerAsWin
) {
5332 aError
.Throw(NS_ERROR_FAILURE
);
5336 CSSIntSize
cssSize(aWidth
, aHeight
);
5337 CheckSecurityWidthAndHeight(&cssSize
.width
, &cssSize
.height
, aCallerType
);
5339 LayoutDeviceIntSize devSize
=
5340 RoundedToInt(cssSize
* CSSToDevScaleForBaseWindow(treeOwnerAsWin
));
5341 aError
= treeOwnerAsWin
->SetSize(devSize
.width
, devSize
.height
, true);
5343 CheckForDPIChange();
5346 void nsGlobalWindowOuter::ResizeByOuter(int32_t aWidthDif
, int32_t aHeightDif
,
5347 CallerType aCallerType
,
5348 ErrorResult
& aError
) {
5350 * If caller is not chrome and the user has not explicitly exempted the site,
5351 * prevent window.resizeBy() by exiting early
5354 if (!CanMoveResizeWindows(aCallerType
) || mBrowsingContext
->IsSubframe()) {
5358 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
5359 if (!treeOwnerAsWin
) {
5360 aError
.Throw(NS_ERROR_FAILURE
);
5364 LayoutDeviceIntSize size
= treeOwnerAsWin
->GetSize();
5366 // To do this correctly we have to convert what we got from GetSize
5367 // into CSS pixels, add the arguments, do the security check, and
5368 // then convert back to device pixels for the call to SetSize.
5370 auto scale
= CSSToDevScaleForBaseWindow(treeOwnerAsWin
);
5371 CSSIntSize cssSize
= RoundedToInt(size
/ scale
);
5373 cssSize
.width
+= aWidthDif
;
5374 cssSize
.height
+= aHeightDif
;
5376 CheckSecurityWidthAndHeight(&cssSize
.width
, &cssSize
.height
, aCallerType
);
5378 LayoutDeviceIntSize newDevSize
= RoundedToInt(cssSize
* scale
);
5380 aError
= treeOwnerAsWin
->SetSize(newDevSize
.width
, newDevSize
.height
, true);
5382 CheckForDPIChange();
5385 void nsGlobalWindowOuter::SizeToContentOuter(
5386 const SizeToContentConstraints
& aConstraints
, ErrorResult
& aError
) {
5391 if (mBrowsingContext
->IsSubframe()) {
5395 // The content viewer does a check to make sure that it's a content
5396 // viewer for a toplevel docshell.
5397 nsCOMPtr
<nsIDocumentViewer
> viewer
;
5398 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
5400 return aError
.Throw(NS_ERROR_FAILURE
);
5403 auto contentSize
= viewer
->GetContentSize(
5404 aConstraints
.mMaxWidth
, aConstraints
.mMaxHeight
, aConstraints
.mPrefWidth
);
5406 return aError
.Throw(NS_ERROR_FAILURE
);
5409 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
= GetTreeOwner();
5411 return aError
.Throw(NS_ERROR_FAILURE
);
5414 // Don't use DevToCSSIntPixelsForBaseWindow() nor
5415 // CSSToDevIntPixelsForBaseWindow() here because contentSize is comes from
5416 // nsIDocumentViewer::GetContentSize() and it's computed with nsPresContext so
5417 // that we need to work with nsPresContext here too.
5418 RefPtr
<nsPresContext
> presContext
= viewer
->GetPresContext();
5421 "Should be non-nullptr if nsIDocumentViewer::GetContentSize() succeeded");
5422 CSSIntSize cssSize
= *contentSize
;
5424 LayoutDeviceIntSize
newDevSize(
5425 presContext
->CSSPixelsToDevPixels(cssSize
.width
),
5426 presContext
->CSSPixelsToDevPixels(cssSize
.height
));
5428 nsCOMPtr
<nsIDocShell
> docShell
= mDocShell
;
5430 treeOwner
->SizeShellTo(docShell
, newDevSize
.width
, newDevSize
.height
);
5433 already_AddRefed
<nsPIWindowRoot
> nsGlobalWindowOuter::GetTopWindowRoot() {
5434 nsPIDOMWindowOuter
* piWin
= GetPrivateRoot();
5439 nsCOMPtr
<nsPIWindowRoot
> window
=
5440 do_QueryInterface(piWin
->GetChromeEventHandler());
5441 return window
.forget();
5444 void nsGlobalWindowOuter::FirePopupBlockedEvent(
5445 Document
* aDoc
, nsIURI
* aPopupURI
, const nsAString
& aPopupWindowName
,
5446 const nsAString
& aPopupWindowFeatures
) {
5449 // Fire a "DOMPopupBlocked" event so that the UI can hear about
5451 PopupBlockedEventInit init
;
5452 init
.mBubbles
= true;
5453 init
.mCancelable
= true;
5454 // XXX: This is a different object, but webidl requires an inner window here
5456 init
.mRequestingWindow
= GetCurrentInnerWindowInternal(this);
5457 init
.mPopupWindowURI
= aPopupURI
;
5458 init
.mPopupWindowName
= aPopupWindowName
;
5459 init
.mPopupWindowFeatures
= aPopupWindowFeatures
;
5461 RefPtr
<PopupBlockedEvent
> event
=
5462 PopupBlockedEvent::Constructor(aDoc
, u
"DOMPopupBlocked"_ns
, init
);
5464 event
->SetTrusted(true);
5466 aDoc
->DispatchEvent(*event
);
5470 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName
) {
5471 // Chrome can set any property.
5472 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
5476 // If the pref is set to true, we can not set the property
5478 return !Preferences::GetBool(aPrefName
, true);
5481 /* If a window open is blocked, fire the appropriate DOM events. */
5482 void nsGlobalWindowOuter::FireAbuseEvents(
5483 const nsACString
& aPopupURL
, const nsAString
& aPopupWindowName
,
5484 const nsAString
& aPopupWindowFeatures
) {
5485 // fetch the URI of the window requesting the opened window
5486 nsCOMPtr
<Document
> currentDoc
= GetDoc();
5487 nsCOMPtr
<nsIURI
> popupURI
;
5489 // build the URI of the would-have-been popup window
5490 // (see nsWindowWatcher::URIfromURL)
5492 // first, fetch the opener's base URI
5494 nsIURI
* baseURL
= nullptr;
5496 nsCOMPtr
<Document
> doc
= GetEntryDocument();
5497 if (doc
) baseURL
= doc
->GetDocBaseURI();
5499 // use the base URI to build what would have been the popup's URI
5500 Unused
<< NS_NewURI(getter_AddRefs(popupURI
), aPopupURL
, nullptr, baseURL
);
5502 // fire an event block full of informative URIs
5503 FirePopupBlockedEvent(currentDoc
, popupURI
, aPopupWindowName
,
5504 aPopupWindowFeatures
);
5507 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::OpenOuter(
5508 const nsAString
& aUrl
, const nsAString
& aName
, const nsAString
& aOptions
,
5509 ErrorResult
& aError
) {
5510 RefPtr
<BrowsingContext
> bc
;
5511 NS_ConvertUTF16toUTF8
url(aUrl
);
5512 nsresult rv
= OpenJS(url
, aName
, aOptions
, getter_AddRefs(bc
));
5513 if (rv
== NS_ERROR_MALFORMED_URI
) {
5514 aError
.ThrowSyntaxError("Unable to open a window with invalid URL '"_ns
+
5519 // XXX Is it possible that some internal errors are thrown here?
5525 return WindowProxyHolder(std::move(bc
));
5528 nsresult
nsGlobalWindowOuter::Open(const nsACString
& aUrl
,
5529 const nsAString
& aName
,
5530 const nsAString
& aOptions
,
5531 nsDocShellLoadState
* aLoadState
,
5532 bool aForceNoOpener
,
5533 BrowsingContext
** _retval
) {
5534 return OpenInternal(aUrl
, aName
, aOptions
,
5536 true, // aCalledNoScript
5537 false, // aDoJSFixups
5540 aLoadState
, aForceNoOpener
, PrintKind::None
, _retval
);
5543 nsresult
nsGlobalWindowOuter::OpenJS(const nsACString
& aUrl
,
5544 const nsAString
& aName
,
5545 const nsAString
& aOptions
,
5546 BrowsingContext
** _retval
) {
5547 return OpenInternal(aUrl
, aName
, aOptions
,
5549 false, // aCalledNoScript
5550 true, // aDoJSFixups
5553 nullptr, // aLoadState
5554 false, // aForceNoOpener
5555 PrintKind::None
, _retval
);
5558 // like Open, but attaches to the new window any extra parameters past
5559 // [features] as a JS property named "arguments"
5560 nsresult
nsGlobalWindowOuter::OpenDialog(const nsACString
& aUrl
,
5561 const nsAString
& aName
,
5562 const nsAString
& aOptions
,
5563 nsIArray
* aArguments
,
5564 BrowsingContext
** _retval
) {
5565 return OpenInternal(aUrl
, aName
, aOptions
,
5567 true, // aCalledNoScript
5568 false, // aDoJSFixups
5570 aArguments
, // Arguments
5571 nullptr, // aLoadState
5572 false, // aForceNoOpener
5573 PrintKind::None
, _retval
);
5576 // Like Open, but passes aNavigate=false.
5578 nsresult
nsGlobalWindowOuter::OpenNoNavigate(const nsACString
& aUrl
,
5579 const nsAString
& aName
,
5580 const nsAString
& aOptions
,
5581 BrowsingContext
** _retval
) {
5582 return OpenInternal(aUrl
, aName
, aOptions
,
5584 true, // aCalledNoScript
5585 false, // aDoJSFixups
5588 nullptr, // aLoadState
5589 false, // aForceNoOpener
5590 PrintKind::None
, _retval
);
5593 Nullable
<WindowProxyHolder
> nsGlobalWindowOuter::OpenDialogOuter(
5594 JSContext
* aCx
, const nsAString
& aUrl
, const nsAString
& aName
,
5595 const nsAString
& aOptions
, const Sequence
<JS::Value
>& aExtraArgument
,
5596 ErrorResult
& aError
) {
5597 nsCOMPtr
<nsIJSArgArray
> argvArray
;
5599 NS_CreateJSArgv(aCx
, aExtraArgument
.Length(), aExtraArgument
.Elements(),
5600 getter_AddRefs(argvArray
));
5601 if (aError
.Failed()) {
5605 RefPtr
<BrowsingContext
> dialog
;
5606 aError
= OpenInternal(NS_ConvertUTF16toUTF8(aUrl
), aName
, aOptions
,
5608 false, // aCalledNoScript
5609 false, // aDoJSFixups
5611 argvArray
, // Arguments
5612 nullptr, // aLoadState
5613 false, // aForceNoOpener
5614 PrintKind::None
, getter_AddRefs(dialog
));
5618 return WindowProxyHolder(std::move(dialog
));
5621 WindowProxyHolder
nsGlobalWindowOuter::GetFramesOuter() {
5622 RefPtr
<nsPIDOMWindowOuter
> frames(this);
5623 FlushPendingNotifications(FlushType::ContentAndNotify
);
5624 return WindowProxyHolder(mBrowsingContext
);
5628 bool nsGlobalWindowOuter::GatherPostMessageData(
5629 JSContext
* aCx
, const nsAString
& aTargetOrigin
, BrowsingContext
** aSource
,
5630 nsAString
& aOrigin
, nsIURI
** aTargetOriginURI
,
5631 nsIPrincipal
** aCallerPrincipal
, nsGlobalWindowInner
** aCallerInnerWindow
,
5632 nsIURI
** aCallerURI
, Maybe
<nsID
>* aCallerAgentClusterId
,
5633 nsACString
* aScriptLocation
, ErrorResult
& aError
) {
5635 // Window.postMessage is an intentional subversion of the same-origin policy.
5636 // As such, this code must be particularly careful in the information it
5637 // exposes to calling code.
5639 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5642 // First, get the caller's window
5643 RefPtr
<nsGlobalWindowInner
> callerInnerWin
=
5644 nsContentUtils::IncumbentInnerWindow();
5645 nsIPrincipal
* callerPrin
;
5646 if (callerInnerWin
) {
5647 RefPtr
<Document
> doc
= callerInnerWin
->GetExtantDoc();
5651 NS_IF_ADDREF(*aCallerURI
= doc
->GetDocumentURI());
5653 // Compute the caller's origin either from its principal or, in the case the
5654 // principal doesn't carry a URI (e.g. the system principal), the caller's
5655 // document. We must get this now instead of when the event is created and
5656 // dispatched, because ultimately it is the identity of the calling window
5657 // *now* that determines who sent the message (and not an identity which
5658 // might have changed due to intervening navigations).
5659 callerPrin
= callerInnerWin
->GetPrincipal();
5661 // In case the global is not a window, it can be a sandbox, and the
5662 // sandbox's principal can be used for the security check.
5663 nsIGlobalObject
* global
= GetIncumbentGlobal();
5664 NS_ASSERTION(global
, "Why is there no global object?");
5665 callerPrin
= global
->PrincipalOrNull();
5667 BasePrincipal::Cast(callerPrin
)->GetScriptLocation(*aScriptLocation
);
5674 // if the principal has a URI, use that to generate the origin
5675 if (!callerPrin
->IsSystemPrincipal()) {
5676 nsAutoCString webExposedOriginSerialization
;
5677 callerPrin
->GetWebExposedOriginSerialization(webExposedOriginSerialization
);
5678 CopyUTF8toUTF16(webExposedOriginSerialization
, aOrigin
);
5679 } else if (callerInnerWin
) {
5683 // otherwise use the URI of the document to generate origin
5684 nsContentUtils::GetWebExposedOriginSerialization(*aCallerURI
, aOrigin
);
5686 // in case of a sandbox with a system principal origin can be empty
5687 if (!callerPrin
->IsSystemPrincipal()) {
5691 NS_IF_ADDREF(*aCallerPrincipal
= callerPrin
);
5693 // "/" indicates same origin as caller, "*" indicates no specific origin is
5695 if (!aTargetOrigin
.EqualsASCII("/") && !aTargetOrigin
.EqualsASCII("*")) {
5696 nsCOMPtr
<nsIURI
> targetOriginURI
;
5697 if (NS_FAILED(NS_NewURI(getter_AddRefs(targetOriginURI
), aTargetOrigin
))) {
5698 aError
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
5702 nsresult rv
= NS_MutateURI(targetOriginURI
)
5704 .SetPathQueryRef(""_ns
)
5705 .Finalize(aTargetOriginURI
);
5706 if (NS_FAILED(rv
)) {
5711 if (!nsContentUtils::IsCallerChrome() && callerInnerWin
&&
5712 callerInnerWin
->GetOuterWindowInternal()) {
5713 NS_ADDREF(*aSource
= callerInnerWin
->GetOuterWindowInternal()
5714 ->GetBrowsingContext());
5719 if (aCallerAgentClusterId
&& callerInnerWin
&&
5720 callerInnerWin
->GetDocGroup()) {
5721 *aCallerAgentClusterId
=
5722 Some(callerInnerWin
->GetDocGroup()->AgentClusterId());
5725 callerInnerWin
.forget(aCallerInnerWindow
);
5730 bool nsGlobalWindowOuter::GetPrincipalForPostMessage(
5731 const nsAString
& aTargetOrigin
, nsIURI
* aTargetOriginURI
,
5732 nsIPrincipal
* aCallerPrincipal
, nsIPrincipal
& aSubjectPrincipal
,
5733 nsIPrincipal
** aProvidedPrincipal
) {
5735 // Window.postMessage is an intentional subversion of the same-origin policy.
5736 // As such, this code must be particularly careful in the information it
5737 // exposes to calling code.
5739 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5742 // Convert the provided origin string into a URI for comparison purposes.
5743 nsCOMPtr
<nsIPrincipal
> providedPrincipal
;
5745 if (aTargetOrigin
.EqualsASCII("/")) {
5746 providedPrincipal
= aCallerPrincipal
;
5748 // "*" indicates no specific origin is required.
5749 else if (!aTargetOrigin
.EqualsASCII("*")) {
5750 OriginAttributes attrs
= aSubjectPrincipal
.OriginAttributesRef();
5751 if (aSubjectPrincipal
.IsSystemPrincipal()) {
5752 auto principal
= BasePrincipal::Cast(GetPrincipal());
5754 if (attrs
!= principal
->OriginAttributesRef()) {
5755 nsAutoCString targetURL
;
5756 nsAutoCString sourceOrigin
;
5757 nsAutoCString targetOrigin
;
5759 if (NS_FAILED(principal
->GetAsciiSpec(targetURL
)) ||
5760 NS_FAILED(principal
->GetOrigin(targetOrigin
)) ||
5761 NS_FAILED(aSubjectPrincipal
.GetOrigin(sourceOrigin
))) {
5762 NS_WARNING("Failed to get source and target origins");
5766 nsContentUtils::LogSimpleConsoleError(
5767 NS_ConvertUTF8toUTF16(nsPrintfCString(
5768 R
"(Attempting to post a message to window with url "%s
" and )"
5769 R
"(origin "%s
" from a system principal scope with mismatched )"
5771 targetURL
.get(), targetOrigin
.get(), sourceOrigin
.get())),
5772 "DOM"_ns
, !!principal
->PrivateBrowsingId(),
5773 principal
->IsSystemPrincipal());
5775 attrs
= principal
->OriginAttributesRef();
5779 // Create a nsIPrincipal inheriting the app/browser attributes from the
5782 BasePrincipal::CreateContentPrincipal(aTargetOriginURI
, attrs
);
5783 if (NS_WARN_IF(!providedPrincipal
)) {
5787 // We still need to check the originAttributes if the target origin is '*'.
5788 // But we will ingore the FPD here since the FPDs are possible to be
5790 auto principal
= BasePrincipal::Cast(GetPrincipal());
5791 NS_ENSURE_TRUE(principal
, false);
5793 OriginAttributes targetAttrs
= principal
->OriginAttributesRef();
5794 OriginAttributes sourceAttrs
= aSubjectPrincipal
.OriginAttributesRef();
5795 // We have to exempt the check of OA if the subject prioncipal is a system
5796 // principal since there are many tests try to post messages to content from
5797 // chrome with a mismatch OA. For example, using the ContentTask.spawn() to
5798 // post a message into a private browsing window. The injected code in
5799 // ContentTask.spawn() will be executed under the system principal and the
5800 // OA of the system principal mismatches with the OA of a private browsing
5802 MOZ_DIAGNOSTIC_ASSERT(aSubjectPrincipal
.IsSystemPrincipal() ||
5803 sourceAttrs
.EqualsIgnoringFPD(targetAttrs
));
5805 // If 'privacy.firstparty.isolate.block_post_message' is true, we will block
5806 // postMessage across different first party domains.
5807 if (OriginAttributes::IsBlockPostMessageForFPI() &&
5808 !aSubjectPrincipal
.IsSystemPrincipal() &&
5809 sourceAttrs
.mFirstPartyDomain
!= targetAttrs
.mFirstPartyDomain
) {
5814 providedPrincipal
.forget(aProvidedPrincipal
);
5818 void nsGlobalWindowOuter::PostMessageMozOuter(JSContext
* aCx
,
5819 JS::Handle
<JS::Value
> aMessage
,
5820 const nsAString
& aTargetOrigin
,
5821 JS::Handle
<JS::Value
> aTransfer
,
5822 nsIPrincipal
& aSubjectPrincipal
,
5823 ErrorResult
& aError
) {
5824 RefPtr
<BrowsingContext
> sourceBc
;
5825 nsAutoString origin
;
5826 nsCOMPtr
<nsIURI
> targetOriginURI
;
5827 nsCOMPtr
<nsIPrincipal
> callerPrincipal
;
5828 RefPtr
<nsGlobalWindowInner
> callerInnerWindow
;
5829 nsCOMPtr
<nsIURI
> callerURI
;
5830 Maybe
<nsID
> callerAgentClusterId
= Nothing();
5831 nsAutoCString scriptLocation
;
5832 if (!GatherPostMessageData(
5833 aCx
, aTargetOrigin
, getter_AddRefs(sourceBc
), origin
,
5834 getter_AddRefs(targetOriginURI
), getter_AddRefs(callerPrincipal
),
5835 getter_AddRefs(callerInnerWindow
), getter_AddRefs(callerURI
),
5836 &callerAgentClusterId
, &scriptLocation
, aError
)) {
5840 nsCOMPtr
<nsIPrincipal
> providedPrincipal
;
5841 if (!GetPrincipalForPostMessage(aTargetOrigin
, targetOriginURI
,
5842 callerPrincipal
, aSubjectPrincipal
,
5843 getter_AddRefs(providedPrincipal
))) {
5847 // Create and asynchronously dispatch a runnable which will handle actual DOM
5848 // event creation and dispatch.
5849 RefPtr
<PostMessageEvent
> event
= new PostMessageEvent(
5850 sourceBc
, origin
, this, providedPrincipal
,
5851 callerInnerWindow
? callerInnerWindow
->WindowID() : 0, callerURI
,
5852 scriptLocation
, callerAgentClusterId
);
5854 JS::CloneDataPolicy clonePolicy
;
5856 if (GetDocGroup() && callerAgentClusterId
.isSome() &&
5857 GetDocGroup()->AgentClusterId().Equals(callerAgentClusterId
.value())) {
5858 clonePolicy
.allowIntraClusterClonableSharedObjects();
5861 if (callerInnerWindow
&& callerInnerWindow
->IsSharedMemoryAllowed()) {
5862 clonePolicy
.allowSharedMemoryObjects();
5865 event
->Write(aCx
, aMessage
, aTransfer
, clonePolicy
, aError
);
5866 if (NS_WARN_IF(aError
.Failed())) {
5870 event
->DispatchToTargetThread(aError
);
5873 class nsCloseEvent
: public Runnable
{
5874 RefPtr
<nsGlobalWindowOuter
> mWindow
;
5877 nsCloseEvent(nsGlobalWindowOuter
* aWindow
, bool aIndirect
)
5878 : mozilla::Runnable("nsCloseEvent"),
5880 mIndirect(aIndirect
) {}
5883 static nsresult
PostCloseEvent(nsGlobalWindowOuter
* aWindow
, bool aIndirect
) {
5884 nsCOMPtr
<nsIRunnable
> ev
= new nsCloseEvent(aWindow
, aIndirect
);
5885 return aWindow
->Dispatch(ev
.forget());
5888 NS_IMETHOD
Run() override
{
5891 return PostCloseEvent(mWindow
, false);
5893 mWindow
->ReallyCloseWindow();
5899 bool nsGlobalWindowOuter::CanClose() {
5901 nsCOMPtr
<nsIBrowserDOMWindow
> bwin
= GetBrowserDOMWindow();
5903 bool canClose
= true;
5904 if (bwin
&& NS_SUCCEEDED(bwin
->CanClose(&canClose
))) {
5913 nsCOMPtr
<nsIDocumentViewer
> viewer
;
5914 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
5917 nsresult rv
= viewer
->PermitUnload(&canClose
);
5918 if (NS_SUCCEEDED(rv
) && !canClose
) return false;
5921 // If we still have to print, we delay the closing until print has happened.
5922 if (mShouldDelayPrintUntilAfterLoad
&& mDelayedPrintUntilAfterLoad
) {
5923 mDelayedCloseForPrinting
= true;
5930 void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller
) {
5931 if (!mDocShell
|| IsInModalState() || mBrowsingContext
->IsSubframe()) {
5932 // window.close() is called on a frame in a frameset, on a window
5933 // that's already closed, or on a window for which there's
5934 // currently a modal dialog open. Ignore such calls.
5938 if (mHavePendingClose
) {
5939 // We're going to be closed anyway; do nothing since we don't want
5944 if (mBlockScriptedClosingFlag
) {
5945 // A script's popup has been blocked and we don't want
5946 // the window to be closed directly after this event,
5947 // so the user can see that there was a blocked popup.
5951 // Don't allow scripts from content to close non-neterror windows that
5952 // were not opened by script.
5955 nsresult rv
= mDoc
->GetURL(url
);
5956 NS_ENSURE_SUCCESS_VOID(rv
);
5958 if (!StringBeginsWith(url
, u
"about:neterror"_ns
) &&
5959 !mBrowsingContext
->GetTopLevelCreatedByWebContent() &&
5960 !aTrustedCaller
&& !IsOnlyTopLevelDocumentInSHistory()) {
5962 mAllowScriptsToClose
||
5963 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
5965 // We're blocking the close operation
5966 // report localized error msg in JS console
5967 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
5969 mDoc
, // Better name for the category?
5970 nsContentUtils::eDOM_PROPERTIES
,
5971 "WindowCloseByScriptBlockedWarning");
5978 if (!mInClose
&& !mIsClosed
&& !CanClose()) {
5982 // Fire a DOM event notifying listeners that this window is about to
5983 // be closed. The tab UI code may choose to cancel the default
5984 // action for this event, if so, we won't actually close the window
5985 // (since the tab UI code will close the tab in stead). Sure, this
5986 // could be abused by content code, but do we care? I don't think
5989 bool wasInClose
= mInClose
;
5992 if (!DispatchCustomEvent(u
"DOMWindowClose"_ns
, ChromeOnlyDispatch::eYes
)) {
5993 // Someone chose to prevent the default action for this event, if
5994 // so, let's not close this window after all...
5996 mInClose
= wasInClose
;
6003 bool nsGlobalWindowOuter::IsOnlyTopLevelDocumentInSHistory() {
6004 NS_ENSURE_TRUE(mDocShell
&& mBrowsingContext
, false);
6005 // Disabled since IsFrame() is buggy in Fission
6006 // MOZ_ASSERT(mBrowsingContext->IsTop());
6008 if (mozilla::SessionHistoryInParent()) {
6009 return mBrowsingContext
->GetIsSingleToplevelInHistory();
6012 RefPtr
<ChildSHistory
> csh
= nsDocShell::Cast(mDocShell
)->GetSessionHistory();
6013 if (csh
&& csh
->LegacySHistory()) {
6014 return csh
->LegacySHistory()->IsEmptyOrHasEntriesForSingleTopLevelPage();
6020 nsresult
nsGlobalWindowOuter::Close() {
6021 CloseOuter(/* aTrustedCaller = */ true);
6025 void nsGlobalWindowOuter::ForceClose() {
6026 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default
);
6028 if (mBrowsingContext
->IsSubframe() || !mDocShell
) {
6029 // This may be a frame in a frameset, or a window that's already closed.
6030 // Ignore such calls.
6034 if (mHavePendingClose
) {
6035 // We're going to be closed anyway; do nothing since we don't want
6042 DispatchCustomEvent(u
"DOMWindowClose"_ns
, ChromeOnlyDispatch::eYes
);
6047 void nsGlobalWindowOuter::FinalClose() {
6048 // Flag that we were closed.
6051 if (!mBrowsingContext
->IsDiscarded()) {
6052 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext
->SetClosed(true));
6055 // If we get here from CloseOuter then it means that the parent process is
6056 // going to close our window for us. It's just important to set mIsClosed.
6057 if (XRE_GetProcessType() == GeckoProcessType_Content
) {
6061 // This stuff is non-sensical but incredibly fragile. The reasons for the
6062 // behavior here don't make sense today and may not have ever made sense,
6063 // but various bits of frontend code break when you change them. If you need
6064 // to fix up this behavior, feel free to. It's a righteous task, but involves
6065 // wrestling with various download manager tests, frontend code, and possible
6066 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
6069 // In particular, if some inner of |win| is the entry global, we must
6070 // complete _two_ round-trips to the event loop before the call to
6071 // ReallyCloseWindow. This allows setTimeout handlers that are set after
6072 // FinalClose() is called to run before the window is torn down.
6073 nsCOMPtr
<nsPIDOMWindowInner
> entryWindow
=
6074 do_QueryInterface(GetEntryGlobal());
6075 bool indirect
= entryWindow
&& entryWindow
->GetOuterWindow() == this;
6076 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect
))) {
6077 ReallyCloseWindow();
6079 mHavePendingClose
= true;
6083 void nsGlobalWindowOuter::ReallyCloseWindow() {
6084 // Make sure we never reenter this method.
6085 mHavePendingClose
= true;
6087 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin
= GetTreeOwnerWindow();
6088 if (!treeOwnerAsWin
) {
6092 treeOwnerAsWin
->Destroy();
6096 void nsGlobalWindowOuter::SuppressEventHandling() {
6097 if (mSuppressEventHandlingDepth
== 0) {
6098 if (BrowsingContext
* bc
= GetBrowsingContext()) {
6099 bc
->PreOrderWalk([&](BrowsingContext
* aBC
) {
6100 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= aBC
->GetDOMWindow()) {
6101 if (RefPtr
<Document
> doc
= win
->GetExtantDoc()) {
6102 mSuspendedDocs
.AppendElement(doc
);
6103 // Note: Document::SuppressEventHandling will also automatically
6104 // suppress event handling for any in-process sub-documents.
6105 // However, since we need to deal with cases where remote
6106 // BrowsingContexts may be interleaved with in-process ones, we
6107 // still need to walk the entire tree ourselves. This may be
6108 // slightly redundant in some cases, but since event handling
6109 // suppressions maintain a count of current blockers, it does not
6110 // cause any problems.
6111 doc
->SuppressEventHandling();
6117 mSuppressEventHandlingDepth
++;
6120 void nsGlobalWindowOuter::UnsuppressEventHandling() {
6121 MOZ_ASSERT(mSuppressEventHandlingDepth
!= 0);
6122 mSuppressEventHandlingDepth
--;
6124 if (mSuppressEventHandlingDepth
== 0 && mSuspendedDocs
.Length()) {
6125 RefPtr
<Document
> currentDoc
= GetExtantDoc();
6126 bool fireEvent
= currentDoc
== mSuspendedDocs
[0];
6127 nsTArray
<RefPtr
<Document
>> suspendedDocs
= std::move(mSuspendedDocs
);
6128 for (const auto& doc
: suspendedDocs
) {
6129 doc
->UnsuppressEventHandlingAndFireEvents(fireEvent
);
6134 nsGlobalWindowOuter
* nsGlobalWindowOuter::EnterModalState() {
6135 // GetInProcessScriptableTop, not GetInProcessTop, so that EnterModalState
6136 // works properly with <iframe mozbrowser>.
6137 nsGlobalWindowOuter
* topWin
= GetInProcessScriptableTopInternal();
6140 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
6144 // If there is an active ESM in this window, clear it. Otherwise, this can
6145 // cause a problem if a modal state is entered during a mouseup event.
6146 EventStateManager
* activeESM
= static_cast<EventStateManager
*>(
6147 EventStateManager::GetActiveEventStateManager());
6148 if (activeESM
&& activeESM
->GetPresContext()) {
6149 PresShell
* activePresShell
= activeESM
->GetPresContext()->GetPresShell();
6150 if (activePresShell
&& (nsContentUtils::ContentIsCrossDocDescendantOf(
6151 activePresShell
->GetDocument(), mDoc
) ||
6152 nsContentUtils::ContentIsCrossDocDescendantOf(
6153 mDoc
, activePresShell
->GetDocument()))) {
6154 EventStateManager::ClearGlobalActiveContent(activeESM
);
6156 PresShell::ReleaseCapturingContent();
6158 if (activePresShell
) {
6159 RefPtr
<nsFrameSelection
> frameSelection
=
6160 activePresShell
->FrameSelection();
6161 frameSelection
->SetDragState(false);
6166 // If there are any drag and drop operations in flight, try to end them.
6167 nsCOMPtr
<nsIDragService
> ds
=
6168 do_GetService("@mozilla.org/widget/dragservice;1");
6169 if (ds
&& topWin
->GetDocShell()) {
6170 if (PresShell
* presShell
= topWin
->GetDocShell()->GetPresShell()) {
6171 if (nsViewManager
* vm
= presShell
->GetViewManager()) {
6172 RefPtr
<nsIWidget
> widget
= vm
->GetRootWidget();
6173 if (nsCOMPtr
<nsIDragSession
> session
= ds
->GetCurrentSession(widget
)) {
6174 session
->EndDragSession(true, 0);
6180 // Clear the capturing content if it is under topDoc.
6181 // Usually the activeESM check above does that, but there are cases when
6182 // we don't have activeESM, or it is for different document.
6183 Document
* topDoc
= topWin
->GetExtantDoc();
6184 nsIContent
* capturingContent
= PresShell::GetCapturingContent();
6185 if (capturingContent
&& topDoc
&&
6186 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent
, topDoc
)) {
6187 PresShell::ReleaseCapturingContent();
6190 if (topWin
->mModalStateDepth
== 0) {
6191 topWin
->SuppressEventHandling();
6193 if (nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(topWin
)) {
6197 topWin
->mModalStateDepth
++;
6201 void nsGlobalWindowOuter::LeaveModalState() {
6203 nsGlobalWindowOuter
* topWin
= GetInProcessScriptableTopInternal();
6205 NS_WARNING("Uh, LeaveModalState() called w/o a reachable top window?");
6209 if (topWin
!= this) {
6210 MOZ_ASSERT(IsSuspended());
6211 return topWin
->LeaveModalState();
6215 MOZ_ASSERT(mModalStateDepth
!= 0);
6216 MOZ_ASSERT(IsSuspended());
6219 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
6220 if (mModalStateDepth
== 0) {
6225 UnsuppressEventHandling();
6228 // Remember the time of the last dialog quit.
6229 if (auto* bcg
= GetBrowsingContextGroup()) {
6230 bcg
->SetLastDialogQuitTime(TimeStamp::Now());
6233 if (mModalStateDepth
== 0) {
6234 RefPtr
<Event
> event
= NS_NewDOMEvent(inner
, nullptr, nullptr);
6235 event
->InitEvent(u
"endmodalstate"_ns
, true, false);
6236 event
->SetTrusted(true);
6237 event
->WidgetEventPtr()->mFlags
.mOnlyChromeDispatch
= true;
6238 DispatchEvent(*event
);
6242 bool nsGlobalWindowOuter::IsInModalState() {
6243 nsGlobalWindowOuter
* topWin
= GetInProcessScriptableTopInternal();
6246 // IsInModalState() getting called w/o a reachable top window is a bit
6247 // iffy, but valid enough not to make noise about it. See bug 404828
6251 return topWin
->mModalStateDepth
!= 0;
6254 void nsGlobalWindowOuter::NotifyWindowIDDestroyed(const char* aTopic
) {
6255 nsCOMPtr
<nsIRunnable
> runnable
=
6256 new WindowDestroyedEvent(this, mWindowID
, aTopic
);
6257 Dispatch(runnable
.forget());
6260 Element
* nsGlobalWindowOuter::GetFrameElement(nsIPrincipal
& aSubjectPrincipal
) {
6261 // Per HTML5, the frameElement getter returns null in cross-origin situations.
6262 Element
* element
= GetFrameElement();
6267 if (!aSubjectPrincipal
.SubsumesConsideringDomain(element
->NodePrincipal())) {
6274 Element
* nsGlobalWindowOuter::GetFrameElement() {
6275 if (!mBrowsingContext
|| mBrowsingContext
->IsTop()) {
6278 return mBrowsingContext
->GetEmbedderElement();
6282 class ChildCommandDispatcher
: public Runnable
{
6284 ChildCommandDispatcher(nsPIWindowRoot
* aRoot
, nsIBrowserChild
* aBrowserChild
,
6285 nsPIDOMWindowOuter
* aWindow
, const nsAString
& aAction
)
6286 : mozilla::Runnable("ChildCommandDispatcher"),
6288 mBrowserChild(aBrowserChild
),
6292 NS_IMETHOD
Run() override
{
6293 AutoTArray
<nsCString
, 70> enabledCommands
, disabledCommands
;
6294 mRoot
->GetEnabledDisabledCommands(enabledCommands
, disabledCommands
);
6295 if (enabledCommands
.Length() || disabledCommands
.Length()) {
6296 BrowserChild
* bc
= static_cast<BrowserChild
*>(mBrowserChild
.get());
6297 bc
->SendEnableDisableCommands(mWindow
->GetBrowsingContext(), mAction
,
6298 enabledCommands
, disabledCommands
);
6305 nsCOMPtr
<nsPIWindowRoot
> mRoot
;
6306 nsCOMPtr
<nsIBrowserChild
> mBrowserChild
;
6307 nsCOMPtr
<nsPIDOMWindowOuter
> mWindow
;
6311 class CommandDispatcher
: public Runnable
{
6313 CommandDispatcher(nsIDOMXULCommandDispatcher
* aDispatcher
,
6314 const nsAString
& aAction
)
6315 : mozilla::Runnable("CommandDispatcher"),
6316 mDispatcher(aDispatcher
),
6319 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230, bug 1535398)
6320 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
6321 return mDispatcher
->UpdateCommands(mAction
);
6324 const nsCOMPtr
<nsIDOMXULCommandDispatcher
> mDispatcher
;
6327 } // anonymous namespace
6329 void nsGlobalWindowOuter::UpdateCommands(const nsAString
& anAction
) {
6330 // If this is a child process, redirect to the parent process.
6331 if (nsIDocShell
* docShell
= GetDocShell()) {
6332 if (nsCOMPtr
<nsIBrowserChild
> child
= docShell
->GetBrowserChild()) {
6333 nsCOMPtr
<nsPIWindowRoot
> root
= GetTopWindowRoot();
6335 nsContentUtils::AddScriptRunner(
6336 new ChildCommandDispatcher(root
, child
, this, anAction
));
6342 nsPIDOMWindowOuter
* rootWindow
= GetPrivateRoot();
6347 Document
* doc
= rootWindow
->GetExtantDoc();
6353 // Retrieve the command dispatcher and call updateCommands on it.
6354 nsIDOMXULCommandDispatcher
* xulCommandDispatcher
=
6355 doc
->GetCommandDispatcher();
6356 if (xulCommandDispatcher
) {
6357 nsContentUtils::AddScriptRunner(
6358 new CommandDispatcher(xulCommandDispatcher
, anAction
));
6362 Selection
* nsGlobalWindowOuter::GetSelectionOuter() {
6367 PresShell
* presShell
= mDocShell
->GetPresShell();
6371 return presShell
->GetCurrentSelection(SelectionType::eNormal
);
6374 already_AddRefed
<Selection
> nsGlobalWindowOuter::GetSelection() {
6375 RefPtr
<Selection
> selection
= GetSelectionOuter();
6376 return selection
.forget();
6379 bool nsGlobalWindowOuter::FindOuter(const nsAString
& aString
,
6380 bool aCaseSensitive
, bool aBackwards
,
6381 bool aWrapAround
, bool aWholeWord
,
6382 bool aSearchInFrames
, bool aShowDialog
,
6383 ErrorResult
& aError
) {
6384 Unused
<< aShowDialog
;
6386 nsCOMPtr
<nsIWebBrowserFind
> finder(do_GetInterface(mDocShell
));
6388 aError
.Throw(NS_ERROR_NOT_AVAILABLE
);
6392 // Set the options of the search
6393 aError
= finder
->SetSearchString(aString
);
6394 if (aError
.Failed()) {
6397 finder
->SetMatchCase(aCaseSensitive
);
6398 finder
->SetFindBackwards(aBackwards
);
6399 finder
->SetWrapFind(aWrapAround
);
6400 finder
->SetEntireWord(aWholeWord
);
6401 finder
->SetSearchFrames(aSearchInFrames
);
6403 // the nsIWebBrowserFind is initialized to use this window
6404 // as the search root, but uses focus to set the current search
6405 // frame. If we're being called from JS (as here), this window
6406 // should be the current search frame.
6407 nsCOMPtr
<nsIWebBrowserFindInFrames
> framesFinder(do_QueryInterface(finder
));
6409 framesFinder
->SetRootSearchFrame(this); // paranoia
6410 framesFinder
->SetCurrentSearchFrame(this);
6413 if (aString
.IsEmpty()) {
6417 // Launch the search with the passed in search string
6418 bool didFind
= false;
6419 aError
= finder
->FindNext(&didFind
);
6423 //*****************************************************************************
6425 //*****************************************************************************
6427 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() {
6431 nsIGlobalObject
* nsGlobalWindowOuter::GetOwnerGlobal() const {
6432 return GetCurrentInnerWindowInternal(this);
6435 bool nsGlobalWindowOuter::DispatchEvent(Event
& aEvent
, CallerType aCallerType
,
6437 FORWARD_TO_INNER(DispatchEvent
, (aEvent
, aCallerType
, aRv
), false);
6440 bool nsGlobalWindowOuter::ComputeDefaultWantsUntrusted(ErrorResult
& aRv
) {
6441 // It's OK that we just return false here on failure to create an
6442 // inner. GetOrCreateListenerManager() will likewise fail, and then
6443 // we won't be adding any listeners anyway.
6444 FORWARD_TO_INNER_CREATE(ComputeDefaultWantsUntrusted
, (aRv
), false);
6447 EventListenerManager
* nsGlobalWindowOuter::GetOrCreateListenerManager() {
6448 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager
, (), nullptr);
6451 EventListenerManager
* nsGlobalWindowOuter::GetExistingListenerManager() const {
6452 FORWARD_TO_INNER(GetExistingListenerManager
, (), nullptr);
6455 //*****************************************************************************
6456 // nsGlobalWindowOuter::nsPIDOMWindow
6457 //*****************************************************************************
6459 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetPrivateParent() {
6460 nsCOMPtr
<nsPIDOMWindowOuter
> parent
= GetInProcessParent();
6462 if (this == parent
) {
6463 nsCOMPtr
<nsIContent
> chromeElement(do_QueryInterface(mChromeEventHandler
));
6465 return nullptr; // This is ok, just means a null parent.
6467 Document
* doc
= chromeElement
->GetComposedDoc();
6468 if (!doc
) return nullptr; // This is ok, just means a null parent.
6470 return doc
->GetWindow();
6476 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetPrivateRoot() {
6477 nsCOMPtr
<nsPIDOMWindowOuter
> top
= GetInProcessTop();
6479 nsCOMPtr
<nsIContent
> chromeElement(do_QueryInterface(mChromeEventHandler
));
6480 if (chromeElement
) {
6481 Document
* doc
= chromeElement
->GetComposedDoc();
6483 nsCOMPtr
<nsPIDOMWindowOuter
> parent
= doc
->GetWindow();
6485 top
= parent
->GetInProcessTop();
6493 // This has a caller in Windows-only code (nsNativeAppSupportWin).
6494 Location
* nsGlobalWindowOuter::GetLocation() {
6495 // This method can be called on the outer window as well.
6496 FORWARD_TO_INNER(Location
, (), nullptr);
6499 void nsGlobalWindowOuter::SetIsBackground(bool aIsBackground
) {
6500 bool changed
= aIsBackground
!= IsBackground();
6501 SetIsBackgroundInternal(aIsBackground
);
6503 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
6505 if (inner
&& changed
) {
6506 inner
->UpdateBackgroundState();
6509 if (aIsBackground
) {
6510 // Notify gamepadManager we are at the background window,
6511 // we need to stop vibrate.
6512 // Stop the vr telemery time spent when it switches to
6513 // the background window.
6514 if (inner
&& changed
) {
6515 inner
->StopGamepadHaptics();
6516 inner
->StopVRActivity();
6517 // true is for asking to set the delta time to
6519 inner
->ResetVRTelemetry(true);
6525 // When switching to be as a top tab, restart the telemetry.
6526 // false is for only resetting the timestamp.
6527 inner
->ResetVRTelemetry(false);
6528 inner
->SyncGamepadState();
6529 inner
->StartVRActivity();
6533 void nsGlobalWindowOuter::SetIsBackgroundInternal(bool aIsBackground
) {
6534 mIsBackground
= aIsBackground
;
6537 void nsGlobalWindowOuter::SetChromeEventHandler(
6538 EventTarget
* aChromeEventHandler
) {
6539 SetChromeEventHandlerInternal(aChromeEventHandler
);
6540 // update the chrome event handler on all our inner windows
6541 RefPtr
<nsGlobalWindowInner
> inner
;
6542 for (PRCList
* node
= PR_LIST_HEAD(this); node
!= this;
6543 node
= PR_NEXT_LINK(inner
)) {
6544 // This cast is only safe if `node != this`, as nsGlobalWindowOuter is also
6546 inner
= static_cast<nsGlobalWindowInner
*>(node
);
6547 NS_ASSERTION(!inner
->mOuterWindow
|| inner
->mOuterWindow
== this,
6548 "bad outer window pointer");
6549 inner
->SetChromeEventHandlerInternal(aChromeEventHandler
);
6553 void nsGlobalWindowOuter::SetFocusedElement(Element
* aElement
,
6554 uint32_t aFocusMethod
,
6556 FORWARD_TO_INNER_VOID(SetFocusedElement
,
6557 (aElement
, aFocusMethod
, aNeedsFocus
));
6560 uint32_t nsGlobalWindowOuter::GetFocusMethod() {
6561 FORWARD_TO_INNER(GetFocusMethod
, (), 0);
6564 bool nsGlobalWindowOuter::ShouldShowFocusRing() {
6565 FORWARD_TO_INNER(ShouldShowFocusRing
, (), false);
6568 bool nsGlobalWindowOuter::TakeFocus(bool aFocus
, uint32_t aFocusMethod
) {
6569 FORWARD_TO_INNER(TakeFocus
, (aFocus
, aFocusMethod
), false);
6572 void nsGlobalWindowOuter::SetReadyForFocus() {
6573 FORWARD_TO_INNER_VOID(SetReadyForFocus
, ());
6576 void nsGlobalWindowOuter::PageHidden(bool aIsEnteringBFCacheInParent
) {
6577 FORWARD_TO_INNER_VOID(PageHidden
, (aIsEnteringBFCacheInParent
));
6580 already_AddRefed
<nsICSSDeclaration
>
6581 nsGlobalWindowOuter::GetComputedStyleHelperOuter(Element
& aElt
,
6582 const nsAString
& aPseudoElt
,
6583 bool aDefaultStylesOnly
,
6589 RefPtr
<nsICSSDeclaration
> compStyle
= NS_NewComputedDOMStyle(
6590 &aElt
, aPseudoElt
, mDoc
,
6591 aDefaultStylesOnly
? nsComputedDOMStyle::StyleType::DefaultOnly
6592 : nsComputedDOMStyle::StyleType::All
,
6595 return compStyle
.forget();
6598 //*****************************************************************************
6599 // nsGlobalWindowOuter::nsIInterfaceRequestor
6600 //*****************************************************************************
6602 nsresult
nsGlobalWindowOuter::GetInterfaceInternal(const nsIID
& aIID
,
6604 NS_ENSURE_ARG_POINTER(aSink
);
6607 if (aIID
.Equals(NS_GET_IID(nsIWebNavigation
))) {
6608 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(mDocShell
));
6609 webNav
.forget(aSink
);
6610 } else if (aIID
.Equals(NS_GET_IID(nsIDocShell
))) {
6611 nsCOMPtr
<nsIDocShell
> docShell
= mDocShell
;
6612 docShell
.forget(aSink
);
6615 else if (aIID
.Equals(NS_GET_IID(nsIWebBrowserPrint
))) {
6617 nsCOMPtr
<nsIDocumentViewer
> viewer
;
6618 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
6620 nsCOMPtr
<nsIWebBrowserPrint
> webBrowserPrint(do_QueryInterface(viewer
));
6621 webBrowserPrint
.forget(aSink
);
6626 else if (aIID
.Equals(NS_GET_IID(nsILoadContext
))) {
6627 nsCOMPtr
<nsILoadContext
> loadContext(do_QueryInterface(mDocShell
));
6628 loadContext
.forget(aSink
);
6631 return *aSink
? NS_OK
: NS_ERROR_NO_INTERFACE
;
6635 nsGlobalWindowOuter::GetInterface(const nsIID
& aIID
, void** aSink
) {
6636 nsresult rv
= GetInterfaceInternal(aIID
, aSink
);
6637 if (rv
== NS_ERROR_NO_INTERFACE
) {
6638 return QueryInterface(aIID
, aSink
);
6643 bool nsGlobalWindowOuter::IsSuspended() const {
6644 MOZ_ASSERT(NS_IsMainThread());
6645 // No inner means we are effectively suspended
6646 if (!mInnerWindow
) {
6649 return nsGlobalWindowInner::Cast(mInnerWindow
)->IsSuspended();
6652 bool nsGlobalWindowOuter::IsFrozen() const {
6653 MOZ_ASSERT(NS_IsMainThread());
6654 // No inner means we are effectively frozen
6655 if (!mInnerWindow
) {
6658 return nsGlobalWindowInner::Cast(mInnerWindow
)->IsFrozen();
6661 nsresult
nsGlobalWindowOuter::FireDelayedDOMEvents(bool aIncludeSubWindows
) {
6662 FORWARD_TO_INNER(FireDelayedDOMEvents
, (aIncludeSubWindows
),
6663 NS_ERROR_UNEXPECTED
);
6666 //*****************************************************************************
6667 // nsGlobalWindowOuter: Window Control Functions
6668 //*****************************************************************************
6670 nsPIDOMWindowOuter
* nsGlobalWindowOuter::GetInProcessParentInternal() {
6671 nsCOMPtr
<nsPIDOMWindowOuter
> parent
= GetInProcessParent();
6673 if (parent
&& parent
!= this) {
6680 void nsGlobalWindowOuter::UnblockScriptedClosing() {
6681 mBlockScriptedClosingFlag
= false;
6684 class AutoUnblockScriptClosing
{
6686 RefPtr
<nsGlobalWindowOuter
> mWin
;
6689 explicit AutoUnblockScriptClosing(nsGlobalWindowOuter
* aWin
) : mWin(aWin
) {
6692 ~AutoUnblockScriptClosing() {
6693 void (nsGlobalWindowOuter::*run
)() =
6694 &nsGlobalWindowOuter::UnblockScriptedClosing
;
6695 nsCOMPtr
<nsIRunnable
> caller
= NewRunnableMethod(
6696 "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin
, run
);
6697 mWin
->Dispatch(caller
.forget());
6701 nsresult
nsGlobalWindowOuter::OpenInternal(
6702 const nsACString
& aUrl
, const nsAString
& aName
, const nsAString
& aOptions
,
6703 bool aDialog
, bool aCalledNoScript
, bool aDoJSFixups
, bool aNavigate
,
6704 nsIArray
* aArguments
, nsDocShellLoadState
* aLoadState
, bool aForceNoOpener
,
6705 PrintKind aPrintKind
, BrowsingContext
** aReturn
) {
6706 mozilla::Maybe
<AutoUnblockScriptClosing
> closeUnblocker
;
6708 // Calls to window.open from script should navigate.
6709 MOZ_ASSERT(aCalledNoScript
|| aNavigate
);
6713 nsCOMPtr
<nsIWebBrowserChrome
> chrome
= GetWebBrowserChrome();
6715 // No chrome means we don't want to go through with this open call
6716 // -- see nsIWindowWatcher.idl
6717 return NS_ERROR_NOT_AVAILABLE
;
6720 NS_ASSERTION(mDocShell
, "Must have docshell here");
6722 NS_ConvertUTF16toUTF8
optionsUtf8(aOptions
);
6724 WindowFeatures features
;
6725 if (!features
.Tokenize(optionsUtf8
)) {
6726 return NS_ERROR_FAILURE
;
6729 bool forceNoOpener
= aForceNoOpener
;
6730 if (features
.Exists("noopener")) {
6731 forceNoOpener
= features
.GetBool("noopener");
6732 features
.Remove("noopener");
6735 bool forceNoReferrer
= false;
6736 if (features
.Exists("noreferrer")) {
6737 forceNoReferrer
= features
.GetBool("noreferrer");
6738 if (forceNoReferrer
) {
6739 // noreferrer implies noopener
6740 forceNoOpener
= true;
6742 features
.Remove("noreferrer");
6745 nsAutoCString options
;
6746 features
.Stringify(options
);
6748 // If noopener is force-enabled for the current document, then set noopener to
6749 // true, and clear the name to "_blank".
6750 nsAutoString
windowName(aName
);
6751 if (nsDocShell::Cast(GetDocShell())->NoopenerForceEnabled() &&
6752 aPrintKind
== PrintKind::None
) {
6753 MOZ_DIAGNOSTIC_ASSERT(aNavigate
,
6754 "cannot OpenNoNavigate if noopener is force-enabled");
6756 forceNoOpener
= true;
6757 windowName
= u
"_blank"_ns
;
6760 bool windowExists
= WindowExists(windowName
, forceNoOpener
, !aCalledNoScript
);
6762 // XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
6763 // (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run.
6764 // But note that if you change this to GetEntryGlobal(), say, then
6765 // OnLinkClickEvent::Run will need a full-blown AutoEntryScript. (Bug 1930445)
6766 const bool checkForPopup
= [&]() {
6773 if (aLoadState
&& aLoadState
->IsFormSubmission()) {
6776 return !nsContentUtils::LegacyIsCallerChromeOrNativeCode();
6779 nsCOMPtr
<nsIURI
> uri
;
6781 // It's important to do this security check before determining whether this
6782 // window opening should be blocked, to ensure that we don't FireAbuseEvents
6783 // for a window opening that wouldn't have succeeded in the first place.
6784 if (!aUrl
.IsEmpty()) {
6785 // It's safe to skip the security check below if we're a dialog because
6786 // window.openDialog is not callable from content script. See bug 56851.
6788 // If we're not navigating, we assume that whoever *does* navigate the
6789 // window will do a security check of their own.
6791 URIfromURLAndMaybeDoSecurityCheck(aUrl
, !aDialog
&& aNavigate
);
6792 if (result
.isErr()) {
6793 return result
.unwrapErr();
6796 uri
= result
.unwrap();
6798 mDoc
->SetUseCounter(eUseCounter_custom_WindowOpenEmptyUrl
);
6801 UserActivation::Modifiers modifiers
;
6802 mBrowsingContext
->GetUserActivationModifiersForPopup(&modifiers
);
6804 // Need to create loadState before the user activation is consumed in
6805 // BrowsingContext::RevisePopupAbuseLevel() below.
6806 RefPtr
<nsDocShellLoadState
> loadState
= aLoadState
;
6807 if (!loadState
&& aNavigate
&& uri
) {
6808 loadState
= nsWindowWatcher::CreateLoadState(uri
, this);
6811 PopupBlocker::PopupControlState abuseLevel
=
6812 PopupBlocker::GetPopupControlState();
6813 if (checkForPopup
) {
6814 abuseLevel
= mBrowsingContext
->RevisePopupAbuseLevel(abuseLevel
);
6815 if (abuseLevel
>= PopupBlocker::openBlocked
) {
6816 if (!aCalledNoScript
) {
6817 // If script in some other window is doing a window.open on us and
6818 // it's being blocked, then it's OK to close us afterwards, probably.
6819 // But if we're doing a window.open on ourselves and block the popup,
6820 // prevent this window from closing until after this script terminates
6821 // so that whatever popup blocker UI the app has will be visible.
6822 nsCOMPtr
<nsPIDOMWindowInner
> entryWindow
=
6823 do_QueryInterface(GetEntryGlobal());
6824 // Note that entryWindow can be null here if some JS component was the
6825 // place where script was entered for this JS execution.
6826 if (entryWindow
&& entryWindow
->GetOuterWindow() == this) {
6827 mBlockScriptedClosingFlag
= true;
6828 closeUnblocker
.emplace(this);
6832 FireAbuseEvents(aUrl
, windowName
, aOptions
);
6833 return aDoJSFixups
? NS_OK
: NS_ERROR_FAILURE
;
6837 // Per https://github.com/whatwg/html/pull/10547, we should always consume
6838 // user activation when opening a new window, even if the popup blocker is
6839 // disabled or the website has popup permission.
6840 if (!windowExists
&& mDoc
) {
6841 mDoc
->ConsumeTransientUserGestureActivation();
6844 RefPtr
<BrowsingContext
> domReturn
;
6846 nsresult rv
= NS_OK
;
6847 nsCOMPtr
<nsIWindowWatcher
> wwatch
=
6848 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
6849 NS_ENSURE_TRUE(wwatch
, rv
);
6851 NS_ConvertUTF16toUTF8
name(windowName
);
6853 nsCOMPtr
<nsPIWindowWatcher
> pwwatch(do_QueryInterface(wwatch
));
6854 NS_ENSURE_STATE(pwwatch
);
6856 MOZ_ASSERT_IF(checkForPopup
, abuseLevel
< PopupBlocker::openBlocked
);
6857 // At this point we should know for a fact that if checkForPopup then
6858 // abuseLevel < PopupBlocker::openBlocked, so we could just check for
6859 // abuseLevel == PopupBlocker::openControlled. But let's be defensive just in
6860 // case and treat anything that fails the above assert as a spam popup too, if
6862 bool isPopupSpamWindow
=
6863 checkForPopup
&& (abuseLevel
>= PopupBlocker::openControlled
);
6865 const auto wwPrintKind
= [&] {
6866 switch (aPrintKind
) {
6867 case PrintKind::None
:
6868 return nsPIWindowWatcher::PRINT_NONE
;
6869 case PrintKind::InternalPrint
:
6870 return nsPIWindowWatcher::PRINT_INTERNAL
;
6871 case PrintKind::WindowDotPrint
:
6872 return nsPIWindowWatcher::PRINT_WINDOW_DOT_PRINT
;
6874 MOZ_ASSERT_UNREACHABLE("Wat");
6875 return nsPIWindowWatcher::PRINT_NONE
;
6879 // Reset popup state while opening a window to prevent the
6880 // current state from being active the whole time a modal
6882 AutoPopupStatePusher
popupStatePusher(PopupBlocker::openAbused
, true);
6884 if (!aCalledNoScript
) {
6885 // We asserted at the top of this function that aNavigate is true for
6886 // !aCalledNoScript.
6887 rv
= pwwatch
->OpenWindow2(this, uri
, name
, options
, modifiers
,
6888 /* aCalledFromScript = */ true, aDialog
,
6889 aNavigate
, aArguments
, isPopupSpamWindow
,
6890 forceNoOpener
, forceNoReferrer
, wwPrintKind
,
6891 loadState
, getter_AddRefs(domReturn
));
6893 // Force a system caller here so that the window watcher won't screw us
6894 // up. We do NOT want this case looking at the JS context on the stack
6895 // when searching. Compare comments on
6896 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
6898 // Note: Because nsWindowWatcher is so broken, it's actually important
6899 // that we don't force a system caller here, because that screws it up
6900 // when it tries to compute the caller principal to associate with dialog
6901 // arguments. That whole setup just really needs to be rewritten. :-(
6902 AutoNoJSAPI nojsapi
;
6903 rv
= pwwatch
->OpenWindow2(this, uri
, name
, options
, modifiers
,
6904 /* aCalledFromScript = */ false, aDialog
,
6905 aNavigate
, aArguments
, isPopupSpamWindow
,
6906 forceNoOpener
, forceNoReferrer
, wwPrintKind
,
6907 loadState
, getter_AddRefs(domReturn
));
6911 NS_ENSURE_SUCCESS(rv
, rv
);
6915 if (!aCalledNoScript
&& !windowExists
&& uri
&& !forceNoOpener
) {
6916 MaybeAllowStorageForOpenedWindow(uri
);
6919 if (domReturn
&& aDoJSFixups
) {
6920 nsPIDOMWindowOuter
* outer
= domReturn
->GetDOMWindow();
6921 if (outer
&& !nsGlobalWindowOuter::Cast(outer
)->IsChromeWindow()) {
6922 // A new non-chrome window was created from a call to
6923 // window.open() from JavaScript, make sure there's a document in
6924 // the new window. We do this by simply asking the new window for
6925 // its document, this will synchronously create an empty document
6926 // if there is no document in the window.
6927 // XXXbz should this just use EnsureInnerWindow()?
6929 // Force document creation.
6930 nsCOMPtr
<Document
> doc
= outer
->GetDoc();
6935 domReturn
.forget(aReturn
);
6939 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI
* aURI
) {
6940 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
6941 if (NS_WARN_IF(!inner
)) {
6945 // No 3rd party URL/window.
6946 if (!AntiTrackingUtils::IsThirdPartyWindow(inner
, aURI
)) {
6950 Document
* doc
= inner
->GetDoc();
6954 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
6955 aURI
, doc
->NodePrincipal()->OriginAttributesRef());
6957 // We don't care when the asynchronous work finishes here.
6958 // Without e10s or fission enabled this is run in the parent process.
6959 if (XRE_IsParentProcess()) {
6960 Unused
<< StorageAccessAPIHelper::AllowAccessForOnParentProcess(
6961 principal
, GetBrowsingContext(), ContentBlockingNotifier::eOpener
);
6963 Unused
<< StorageAccessAPIHelper::AllowAccessForOnChildProcess(
6964 principal
, GetBrowsingContext(), ContentBlockingNotifier::eOpener
);
6968 //*****************************************************************************
6969 // nsGlobalWindowOuter: Helper Functions
6970 //*****************************************************************************
6972 already_AddRefed
<nsIDocShellTreeOwner
> nsPIDOMWindowOuter::GetTreeOwner() {
6973 // If there's no docShellAsItem, this window must have been closed,
6974 // in that case there is no tree owner.
6980 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
6981 mDocShell
->GetTreeOwner(getter_AddRefs(treeOwner
));
6982 return treeOwner
.forget();
6985 already_AddRefed
<nsIBaseWindow
> nsPIDOMWindowOuter::GetTreeOwnerWindow() {
6986 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
6988 // If there's no mDocShell, this window must have been closed,
6989 // in that case there is no tree owner.
6992 mDocShell
->GetTreeOwner(getter_AddRefs(treeOwner
));
6995 nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(treeOwner
);
6996 return baseWindow
.forget();
6999 already_AddRefed
<nsIWebBrowserChrome
>
7000 nsPIDOMWindowOuter::GetWebBrowserChrome() {
7001 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
= GetTreeOwner();
7003 nsCOMPtr
<nsIWebBrowserChrome
> browserChrome
= do_GetInterface(treeOwner
);
7004 return browserChrome
.forget();
7007 ScrollContainerFrame
* nsGlobalWindowOuter::GetScrollContainerFrame() {
7012 PresShell
* presShell
= mDocShell
->GetPresShell();
7014 return presShell
->GetRootScrollContainerFrame();
7019 Result
<already_AddRefed
<nsIURI
>, nsresult
>
7020 nsGlobalWindowOuter::URIfromURLAndMaybeDoSecurityCheck(const nsACString
& aURL
,
7021 bool aSecurityCheck
) {
7022 nsCOMPtr
<nsPIDOMWindowInner
> sourceWindow
=
7023 do_QueryInterface(GetEntryGlobal());
7024 if (!sourceWindow
) {
7025 sourceWindow
= GetCurrentInnerWindow();
7028 // Resolve the baseURI, which could be relative to the calling window.
7030 // Note the algorithm to get the base URI should match the one
7031 // used to actually kick off the load in nsWindowWatcher.cpp.
7032 nsCOMPtr
<Document
> doc
= sourceWindow
->GetDoc();
7033 nsIURI
* baseURI
= nullptr;
7034 auto encoding
= UTF_8_ENCODING
; // default to utf-8
7036 baseURI
= doc
->GetDocBaseURI();
7037 encoding
= doc
->GetDocumentCharacterSet();
7039 nsCOMPtr
<nsIURI
> uri
;
7040 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURL
, encoding
, baseURI
);
7041 if (NS_WARN_IF(NS_FAILED(rv
))) {
7042 return Err(NS_ERROR_DOM_SYNTAX_ERR
);
7045 if (aSecurityCheck
) {
7047 nsGlobalWindowInner
* sourceWin
= nsGlobalWindowInner::Cast(sourceWindow
);
7048 JSAutoRealm
ar(cx
, sourceWin
->GetGlobalJSObject());
7050 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript(
7052 return Err(NS_ERROR_FAILURE
);
7056 return uri
.forget();
7059 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType
) {
7061 mDoc
->FlushPendingNotifications(aType
);
7065 void nsGlobalWindowOuter::EnsureSizeAndPositionUpToDate() {
7066 // If we're a subframe, make sure our size is up to date. Make sure to go
7067 // through the document chain rather than the window chain to not flush on
7068 // detached iframes, see bug 1545516.
7069 if (mDoc
&& mDoc
->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
7070 RefPtr
<Document
> parent
= mDoc
->GetInProcessParentDocument();
7071 parent
->FlushPendingNotifications(FlushType::Layout
);
7075 already_AddRefed
<nsISupports
> nsGlobalWindowOuter::SaveWindowState() {
7076 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7078 if (!mContext
|| !GetWrapperPreserveColor()) {
7079 // The window may be getting torn down; don't bother saving state.
7083 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
7084 NS_ASSERTION(inner
, "No inner window to save");
7086 if (WindowContext
* wc
= inner
->GetWindowContext()) {
7087 MOZ_ASSERT(!wc
->GetWindowStateSaved());
7088 Unused
<< wc
->SetWindowStateSaved(true);
7091 // Don't do anything else to this inner window! After this point, all
7092 // calls to SetTimeoutOrInterval will create entries in the timeout
7093 // list that will only run after this window has come out of the bfcache.
7094 // Also, while we're frozen, we won't dispatch online/offline events
7098 nsCOMPtr
<nsISupports
> state
= new WindowStateHolder(inner
);
7100 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
7101 ("saving window state, state = %p", (void*)state
));
7103 return state
.forget();
7106 nsresult
nsGlobalWindowOuter::RestoreWindowState(nsISupports
* aState
) {
7107 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7109 if (!mContext
|| !GetWrapperPreserveColor()) {
7110 // The window may be getting torn down; don't bother restoring state.
7114 nsCOMPtr
<WindowStateHolder
> holder
= do_QueryInterface(aState
);
7115 NS_ENSURE_TRUE(holder
, NS_ERROR_FAILURE
);
7117 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
7118 ("restoring window state, state = %p", (void*)holder
));
7120 // And we're ready to go!
7121 nsGlobalWindowInner
* inner
= GetCurrentInnerWindowInternal(this);
7123 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
7124 // it easy to tell which link was last clicked when going back a page.
7125 RefPtr
<Element
> focusedElement
= inner
->GetFocusedElement();
7126 if (nsContentUtils::ContentIsLink(focusedElement
)) {
7127 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
7128 fm
->SetFocus(focusedElement
, nsIFocusManager::FLAG_NOSCROLL
|
7129 nsIFocusManager::FLAG_SHOWRING
);
7133 if (WindowContext
* wc
= inner
->GetWindowContext()) {
7134 MOZ_ASSERT(wc
->GetWindowStateSaved());
7135 Unused
<< wc
->SetWindowStateSaved(false);
7140 holder
->DidRestoreWindow();
7145 void nsGlobalWindowOuter::AddSizeOfIncludingThis(
7146 nsWindowSizes
& aWindowSizes
) const {
7147 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
7148 aWindowSizes
.mState
.mMallocSizeOf(this);
7151 uint32_t nsGlobalWindowOuter::GetAutoActivateVRDisplayID() {
7152 uint32_t retVal
= mAutoActivateVRDisplayID
;
7153 mAutoActivateVRDisplayID
= 0;
7157 void nsGlobalWindowOuter::SetAutoActivateVRDisplayID(
7158 uint32_t aAutoActivateVRDisplayID
) {
7159 mAutoActivateVRDisplayID
= aAutoActivateVRDisplayID
;
7162 already_AddRefed
<nsWindowRoot
> nsGlobalWindowOuter::GetWindowRootOuter() {
7163 nsCOMPtr
<nsPIWindowRoot
> root
= GetTopWindowRoot();
7164 return root
.forget().downcast
<nsWindowRoot
>();
7167 nsIDOMWindowUtils
* nsGlobalWindowOuter::WindowUtils() {
7168 if (!mWindowUtils
) {
7169 mWindowUtils
= new nsDOMWindowUtils(this);
7171 return mWindowUtils
;
7174 bool nsGlobalWindowOuter::IsInSyncOperation() {
7175 return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
7178 // Note: This call will lock the cursor, it will not change as it moves.
7179 // To unlock, the cursor must be set back to Auto.
7180 void nsGlobalWindowOuter::SetCursorOuter(const nsACString
& aCursor
,
7181 ErrorResult
& aError
) {
7182 auto cursor
= StyleCursorKind::Auto
;
7183 if (!Servo_CursorKind_Parse(&aCursor
, &cursor
)) {
7184 // FIXME: It's a bit weird that this doesn't throw but stuff below does, but
7185 // matches previous behavior so...
7189 RefPtr
<nsPresContext
> presContext
;
7191 presContext
= mDocShell
->GetPresContext();
7195 // Need root widget.
7196 PresShell
* presShell
= mDocShell
->GetPresShell();
7198 aError
.Throw(NS_ERROR_FAILURE
);
7202 nsViewManager
* vm
= presShell
->GetViewManager();
7204 aError
.Throw(NS_ERROR_FAILURE
);
7208 nsView
* rootView
= vm
->GetRootView();
7210 aError
.Throw(NS_ERROR_FAILURE
);
7214 nsIWidget
* widget
= rootView
->GetNearestWidget(nullptr);
7216 aError
.Throw(NS_ERROR_FAILURE
);
7220 // Call esm and set cursor.
7221 aError
= presContext
->EventStateManager()->SetCursor(
7222 cursor
, nullptr, {}, Nothing(), widget
, true);
7226 nsIBrowserDOMWindow
* nsGlobalWindowOuter::GetBrowserDOMWindow() {
7227 MOZ_RELEASE_ASSERT(IsChromeWindow());
7228 return mChromeFields
.mBrowserDOMWindow
;
7231 void nsGlobalWindowOuter::SetBrowserDOMWindowOuter(
7232 nsIBrowserDOMWindow
* aBrowserWindow
) {
7233 MOZ_ASSERT(IsChromeWindow());
7234 mChromeFields
.mBrowserDOMWindow
= aBrowserWindow
;
7237 ChromeMessageBroadcaster
* nsGlobalWindowOuter::GetMessageManager() {
7238 if (!mInnerWindow
) {
7239 NS_WARNING("No inner window available!");
7242 return GetCurrentInnerWindowInternal(this)->MessageManager();
7245 ChromeMessageBroadcaster
* nsGlobalWindowOuter::GetGroupMessageManager(
7246 const nsAString
& aGroup
) {
7247 if (!mInnerWindow
) {
7248 NS_WARNING("No inner window available!");
7251 return GetCurrentInnerWindowInternal(this)->GetGroupMessageManager(aGroup
);
7254 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline
= NS_IsOffline(); }
7256 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7258 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7259 # error "Never include unwrapped windows.h in this file!"
7262 // Helper called by methods that move/resize the window,
7263 // to ensure the presContext (if any) is aware of resolution
7264 // change that may happen in multi-monitor configuration.
7265 void nsGlobalWindowOuter::CheckForDPIChange() {
7267 RefPtr
<nsPresContext
> presContext
= mDocShell
->GetPresContext();
7269 if (presContext
->DeviceContext()->CheckDPIChange()) {
7270 presContext
->UIResolutionChanged();
7276 nsresult
nsGlobalWindowOuter::Dispatch(
7277 already_AddRefed
<nsIRunnable
>&& aRunnable
) const {
7278 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7279 return NS_DispatchToCurrentThread(std::move(aRunnable
));
7282 nsISerialEventTarget
* nsGlobalWindowOuter::SerialEventTarget() const {
7283 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7284 return GetMainThreadSerialEventTarget();
7287 void nsGlobalWindowOuter::MaybeResetWindowName(Document
* aNewDocument
) {
7288 MOZ_ASSERT(aNewDocument
);
7290 if (!StaticPrefs::privacy_window_name_update_enabled()) {
7294 const LoadingSessionHistoryInfo
* info
=
7295 nsDocShell::Cast(mDocShell
)->GetLoadingSessionHistoryInfo();
7296 if (!info
|| info
->mForceMaybeResetName
.isNothing()) {
7297 // We only reset the window name for the top-level content as well as
7298 // storing in session entries.
7299 if (!GetBrowsingContext()->IsTopContent()) {
7303 // Following implements https://html.spec.whatwg.org/#history-traversal:
7304 // Step 4.2. Check if the loading document has a different origin than the
7305 // previous document.
7307 // We don't need to do anything if we haven't loaded a non-initial document.
7308 if (!GetBrowsingContext()->GetHasLoadedNonInitialDocument()) {
7312 // If we have an existing document, directly check the document prinicpals
7313 // with the new document to know if it is cross-origin.
7315 // Note that there will be an issue of initial document handling in Fission
7316 // when running the WPT unset_context_name-1.html. In the test, the first
7317 // about:blank page would be loaded with the principal of the testing domain
7318 // in Fission and the window.name will be set there. Then, The window.name
7319 // won't be reset after navigating to the testing page because the principal
7320 // is the same. But, it won't be the case for non-Fission mode that the
7321 // first about:blank will be loaded with a null principal and the
7322 // window.name will be reset when loading the test page.
7323 if (mDoc
&& mDoc
->NodePrincipal()->Equals(aNewDocument
->NodePrincipal())) {
7327 // If we don't have an existing document, and if it's not the initial
7328 // about:blank, we could be loading a document because of the
7329 // process-switching. In this case, this should be a cross-origin
7331 } else if (!info
->mForceMaybeResetName
.ref()) {
7335 // Step 4.2.2 Store the window.name into all session history entries that have
7336 // the same origin as the previous document.
7337 nsDocShell::Cast(mDocShell
)->StoreWindowNameToSHEntries();
7339 // Step 4.2.3 Clear the window.name if the browsing context is the top-level
7340 // content and doesn't have an opener.
7342 // We need to reset the window name in case of a cross-origin navigation,
7343 // without an opener.
7344 RefPtr
<BrowsingContext
> opener
= GetOpenerBrowsingContext();
7349 Unused
<< mBrowsingContext
->SetName(EmptyString());
7352 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
7353 BrowsingContext
* aBC
) {
7354 BrowsingContextGroup
* group
= aBC
->Group();
7357 "nsGlobalWindowOuter::TemporarilyDisableDialogs called without a "
7358 "browsing context group?");
7364 mSavedDialogsEnabled
= group
->GetAreDialogsEnabled();
7365 group
->SetAreDialogsEnabled(false);
7369 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() {
7371 mGroup
->SetAreDialogsEnabled(mSavedDialogsEnabled
);
7376 already_AddRefed
<nsGlobalWindowOuter
> nsGlobalWindowOuter::Create(
7377 nsDocShell
* aDocShell
, bool aIsChrome
) {
7378 uint64_t outerWindowID
= aDocShell
->GetOuterWindowID();
7379 RefPtr
<nsGlobalWindowOuter
> window
= new nsGlobalWindowOuter(outerWindowID
);
7381 window
->mIsChrome
= true;
7383 window
->SetDocShell(aDocShell
);
7385 window
->InitWasOffline();
7386 return window
.forget();
7389 nsIURI
* nsPIDOMWindowOuter::GetDocumentURI() const {
7390 return mDoc
? mDoc
->GetDocumentURI() : mDocumentURI
.get();
7393 void nsPIDOMWindowOuter::MaybeCreateDoc() {
7395 if (nsIDocShell
* docShell
= GetDocShell()) {
7396 // Note that |document| here is the same thing as our mDoc, but we
7397 // don't have to explicitly set the member variable because the docshell
7398 // has already called SetNewDocument().
7399 nsCOMPtr
<Document
> document
= docShell
->GetDocument();
7404 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal(
7405 EventTarget
* aChromeEventHandler
) {
7406 // Out-of-line so we don't need to include ContentFrameMessageManager.h in
7408 mChromeEventHandler
= aChromeEventHandler
;
7410 // mParentTarget and mMessageManager will be set when the next event is
7411 // dispatched or someone asks for our message manager.
7412 mParentTarget
= nullptr;
7413 mMessageManager
= nullptr;
7416 mozilla::dom::DocGroup
* nsPIDOMWindowOuter::GetDocGroup() const {
7417 Document
* doc
= GetExtantDoc();
7419 return doc
->GetDocGroup();
7424 nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID
)
7425 : mFrameElement(nullptr),
7426 mModalStateDepth(0),
7427 mSuppressEventHandlingDepth(0),
7428 mIsBackground(false),
7429 mIsRootOuterWindow(false),
7430 mInnerWindow(nullptr),
7431 mWindowID(aWindowID
),
7432 mMarkedCCGeneration(0) {}
7434 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() = default;