Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / nsGlobalWindowOuter.cpp
blobbd11d1344e6517cc65c7dac1b5d31ce424ae5e2e
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 "nsGlobalWindow.h"
9 #include <algorithm>
11 #include "mozilla/MemoryReporting.h"
13 // Local Includes
14 #include "Navigator.h"
15 #include "nsContentSecurityManager.h"
16 #include "nsScreen.h"
17 #include "nsHistory.h"
18 #include "nsDOMNavigationTiming.h"
19 #include "nsICookieService.h"
20 #include "nsIDOMStorageManager.h"
21 #include "nsIPermission.h"
22 #include "nsIPermissionManager.h"
23 #include "nsIPrefBranch.h"
24 #include "nsISecureBrowserUI.h"
25 #include "nsIWebProgressListener.h"
26 #include "mozilla/AntiTrackingCommon.h"
27 #include "mozilla/dom/ContentFrameMessageManager.h"
28 #include "mozilla/dom/EventTarget.h"
29 #include "mozilla/dom/LocalStorage.h"
30 #include "mozilla/dom/LSObject.h"
31 #include "mozilla/dom/Storage.h"
32 #include "mozilla/dom/MaybeCrossOriginObject.h"
33 #include "mozilla/dom/Performance.h"
34 #include "mozilla/dom/StorageEvent.h"
35 #include "mozilla/dom/StorageEventBinding.h"
36 #include "mozilla/dom/StorageNotifierService.h"
37 #include "mozilla/dom/StorageUtils.h"
38 #include "mozilla/dom/Timeout.h"
39 #include "mozilla/dom/TimeoutHandler.h"
40 #include "mozilla/dom/TimeoutManager.h"
41 #include "mozilla/dom/WindowProxyHolder.h"
42 #include "mozilla/IntegerPrintfMacros.h"
43 #if defined(MOZ_WIDGET_ANDROID)
44 # include "mozilla/dom/WindowOrientationObserver.h"
45 #endif
46 #include "nsBaseCommandController.h"
47 #include "nsError.h"
48 #include "nsISizeOfEventTarget.h"
49 #include "nsDOMJSUtils.h"
50 #include "nsArrayUtils.h"
51 #include "nsDOMWindowList.h"
52 #include "mozilla/dom/WakeLock.h"
53 #include "mozilla/dom/power/PowerManagerService.h"
54 #include "nsIDocShellTreeOwner.h"
55 #include "nsIInterfaceRequestorUtils.h"
56 #include "nsIPermissionManager.h"
57 #include "nsIScriptContext.h"
58 #include "nsIScriptTimeoutHandler.h"
59 #include "nsITimeoutHandler.h"
60 #include "nsISlowScriptDebug.h"
61 #include "nsWindowMemoryReporter.h"
62 #include "nsWindowSizes.h"
63 #include "WindowNamedPropertiesHandler.h"
64 #include "nsFrameSelection.h"
65 #include "nsNetUtil.h"
66 #include "nsVariant.h"
67 #include "nsPrintfCString.h"
68 #include "mozilla/intl/LocaleService.h"
69 #include "WindowDestroyedEvent.h"
70 #include "nsDocShellLoadState.h"
71 #include "mozilla/dom/WindowGlobalChild.h"
73 // Helper Classes
74 #include "nsJSUtils.h"
75 #include "jsapi.h"
76 #include "js/PropertySpec.h"
77 #include "js/Wrapper.h"
78 #include "nsCharSeparatedTokenizer.h"
79 #include "nsReadableUtils.h"
80 #include "nsJSEnvironment.h"
81 #include "mozilla/dom/ScriptSettings.h"
82 #include "mozilla/Preferences.h"
83 #include "mozilla/Likely.h"
84 #include "mozilla/Sprintf.h"
85 #include "mozilla/Unused.h"
87 // Other Classes
88 #include "mozilla/dom/BarProps.h"
89 #include "nsContentCID.h"
90 #include "nsLayoutStatics.h"
91 #include "nsCCUncollectableMarker.h"
92 #include "mozilla/dom/WorkerCommon.h"
93 #include "mozilla/dom/ToJSValue.h"
94 #include "nsJSPrincipals.h"
95 #include "mozilla/Attributes.h"
96 #include "mozilla/Components.h"
97 #include "mozilla/Debug.h"
98 #include "mozilla/EventListenerManager.h"
99 #include "mozilla/EventStates.h"
100 #include "mozilla/MouseEvents.h"
101 #include "mozilla/PresShell.h"
102 #include "mozilla/ProcessHangMonitor.h"
103 #include "mozilla/StaticPrefs.h"
104 #include "mozilla/ThrottledEventQueue.h"
105 #include "AudioChannelService.h"
106 #include "nsAboutProtocolUtils.h"
107 #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
108 #include "PostMessageEvent.h"
109 #include "mozilla/dom/DocGroup.h"
110 #include "mozilla/dom/TabGroup.h"
112 // Interfaces Needed
113 #include "nsIFrame.h"
114 #include "nsCanvasFrame.h"
115 #include "nsIWidget.h"
116 #include "nsIWidgetListener.h"
117 #include "nsIBaseWindow.h"
118 #include "nsIDeviceSensors.h"
119 #include "nsIContent.h"
120 #include "nsIDocShell.h"
121 #include "mozilla/dom/Document.h"
122 #include "Crypto.h"
123 #include "nsDOMString.h"
124 #include "nsIEmbeddingSiteWindow.h"
125 #include "nsThreadUtils.h"
126 #include "nsILoadContext.h"
127 #include "nsIScrollableFrame.h"
128 #include "nsView.h"
129 #include "nsViewManager.h"
130 #include "nsISelectionController.h"
131 #include "nsIPrompt.h"
132 #include "nsIPromptService.h"
133 #include "nsIPromptFactory.h"
134 #include "nsIAddonPolicyService.h"
135 #include "nsIWritablePropertyBag2.h"
136 #include "nsIWebNavigation.h"
137 #include "nsIWebBrowserChrome.h"
138 #include "nsIWebBrowserFind.h" // For window.find()
139 #include "nsIWindowMediator.h" // For window.find()
140 #include "nsComputedDOMStyle.h"
141 #include "nsDOMCID.h"
142 #include "nsDOMWindowUtils.h"
143 #include "nsIWindowWatcher.h"
144 #include "nsPIWindowWatcher.h"
145 #include "nsIContentViewer.h"
146 #include "nsIScriptError.h"
147 #include "nsIControllers.h"
148 #include "nsGlobalWindowCommands.h"
149 #include "nsQueryObject.h"
150 #include "nsContentUtils.h"
151 #include "nsCSSProps.h"
152 #include "nsIURIFixup.h"
153 #include "nsIURIMutator.h"
154 #include "mozilla/EventDispatcher.h"
155 #include "mozilla/EventStateManager.h"
156 #include "nsIObserverService.h"
157 #include "nsFocusManager.h"
158 #include "nsIXULWindow.h"
159 #include "nsITimedChannel.h"
160 #include "nsServiceManagerUtils.h"
161 #include "mozilla/dom/CustomEvent.h"
162 #include "nsIJARChannel.h"
163 #include "nsIScreenManager.h"
164 #include "nsIEffectiveTLDService.h"
165 #include "nsIClassifiedChannel.h"
167 #include "xpcprivate.h"
169 #ifdef NS_PRINTING
170 # include "nsIPrintSettings.h"
171 # include "nsIPrintSettingsService.h"
172 # include "nsIWebBrowserPrint.h"
173 #endif
175 #include "nsWindowRoot.h"
176 #include "nsNetCID.h"
177 #include "nsIArray.h"
179 #include "XULDocument.h"
180 #include "nsIDOMXULCommandDispatcher.h"
182 #include "nsBindingManager.h"
183 #include "nsXBLService.h"
185 #include "nsIDragService.h"
186 #include "mozilla/dom/Element.h"
187 #include "mozilla/dom/Selection.h"
188 #include "nsFrameLoader.h"
189 #include "nsISupportsPrimitives.h"
190 #include "nsXPCOMCID.h"
191 #include "mozilla/Logging.h"
192 #include "prenv.h"
194 #include "mozilla/dom/IDBFactory.h"
195 #include "mozilla/dom/MessageChannel.h"
196 #include "mozilla/dom/Promise.h"
198 #include "mozilla/dom/Gamepad.h"
199 #include "mozilla/dom/GamepadManager.h"
201 #include "gfxVR.h"
202 #include "mozilla/dom/VRDisplay.h"
203 #include "mozilla/dom/VRDisplayEvent.h"
204 #include "mozilla/dom/VRDisplayEventBinding.h"
205 #include "mozilla/dom/VREventObserver.h"
207 #include "nsRefreshDriver.h"
208 #include "Layers.h"
210 #include "mozilla/BasePrincipal.h"
211 #include "mozilla/Services.h"
212 #include "mozilla/Telemetry.h"
213 #include "mozilla/dom/Location.h"
214 #include "nsHTMLDocument.h"
215 #include "nsWrapperCacheInlines.h"
216 #include "mozilla/DOMEventTargetHelper.h"
217 #include "prrng.h"
218 #include "nsSandboxFlags.h"
219 #include "nsBaseCommandController.h"
220 #include "nsXULControllers.h"
221 #include "mozilla/dom/AudioContext.h"
222 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
223 #include "mozilla/dom/cache/CacheStorage.h"
224 #include "mozilla/dom/Console.h"
225 #include "mozilla/dom/Fetch.h"
226 #include "mozilla/dom/FunctionBinding.h"
227 #include "mozilla/dom/HashChangeEvent.h"
228 #include "mozilla/dom/IntlUtils.h"
229 #include "mozilla/dom/PopStateEvent.h"
230 #include "mozilla/dom/PopupBlockedEvent.h"
231 #include "mozilla/dom/PrimitiveConversions.h"
232 #include "mozilla/dom/WindowBinding.h"
233 #include "nsIBrowserChild.h"
234 #include "mozilla/dom/MediaQueryList.h"
235 #include "mozilla/dom/ScriptSettings.h"
236 #include "mozilla/dom/NavigatorBinding.h"
237 #include "mozilla/dom/ImageBitmap.h"
238 #include "mozilla/dom/ImageBitmapBinding.h"
239 #include "mozilla/dom/ServiceWorkerRegistration.h"
240 #include "mozilla/dom/U2F.h"
241 #include "mozilla/dom/WebIDLGlobalNameHash.h"
242 #include "mozilla/dom/Worklet.h"
243 #ifdef HAVE_SIDEBAR
244 # include "mozilla/dom/ExternalBinding.h"
245 #endif
247 #ifdef MOZ_WEBSPEECH
248 # include "mozilla/dom/SpeechSynthesis.h"
249 #endif
251 // Apple system headers seem to have a check() macro. <sigh>
252 #ifdef check
253 class nsIScriptTimeoutHandler;
254 # undef check
255 #endif // check
256 #include "AccessCheck.h"
258 #ifdef ANDROID
259 # include <android/log.h>
260 #endif
262 #ifdef XP_WIN
263 # include <process.h>
264 # define getpid _getpid
265 #else
266 # include <unistd.h> // for getpid()
267 #endif
269 using namespace mozilla;
270 using namespace mozilla::dom;
271 using namespace mozilla::dom::ipc;
272 using mozilla::BasePrincipal;
273 using mozilla::OriginAttributes;
274 using mozilla::TimeStamp;
276 #define FORWARD_TO_INNER(method, args, err_rval) \
277 PR_BEGIN_MACRO \
278 if (!mInnerWindow) { \
279 NS_WARNING("No inner window available!"); \
280 return err_rval; \
282 return GetCurrentInnerWindowInternal()->method args; \
283 PR_END_MACRO
285 #define FORWARD_TO_INNER_VOID(method, args) \
286 PR_BEGIN_MACRO \
287 if (!mInnerWindow) { \
288 NS_WARNING("No inner window available!"); \
289 return; \
291 GetCurrentInnerWindowInternal()->method args; \
292 return; \
293 PR_END_MACRO
295 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an
296 // inner doesn't already exists.
297 #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \
298 PR_BEGIN_MACRO \
299 if (!mInnerWindow) { \
300 if (mIsClosed) { \
301 return err_rval; \
303 nsCOMPtr<Document> kungFuDeathGrip = GetDoc(); \
304 ::mozilla::Unused << kungFuDeathGrip; \
305 if (!mInnerWindow) { \
306 return err_rval; \
309 return GetCurrentInnerWindowInternal()->method args; \
310 PR_END_MACRO
312 static LazyLogModule gDOMLeakPRLogOuter("DOMLeakOuter");
313 extern LazyLogModule gPageCacheLog;
315 static int32_t gOpenPopupSpamCount = 0;
317 static bool gSyncContentBlockingNotifications = false;
319 nsGlobalWindowOuter::OuterWindowByIdTable*
320 nsGlobalWindowOuter::sOuterWindowsById = nullptr;
322 /* static */
323 nsPIDOMWindowOuter* nsPIDOMWindowOuter::GetFromCurrentInner(
324 nsPIDOMWindowInner* aInner) {
325 if (!aInner) {
326 return nullptr;
329 nsPIDOMWindowOuter* outer = aInner->GetOuterWindow();
330 if (!outer || outer->GetCurrentInnerWindow() != aInner) {
331 return nullptr;
334 return outer;
337 //*****************************************************************************
338 // nsOuterWindowProxy: Outer Window Proxy
339 //*****************************************************************************
341 // Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so
342 // JSObject::swap can swap it with CrossCompartmentWrappers without requiring
343 // malloc.
345 // We store the nsGlobalWindowOuter* in our first slot.
347 // We store our holder weakmap in the second slot.
348 const js::Class OuterWindowProxyClass = PROXY_CLASS_DEF(
349 "Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
351 static const size_t OUTER_WINDOW_SLOT = 0;
352 static const size_t HOLDER_WEAKMAP_SLOT = 1;
354 class nsOuterWindowProxy : public MaybeCrossOriginObject<js::Wrapper> {
355 typedef MaybeCrossOriginObject<js::Wrapper> Base;
357 public:
358 constexpr nsOuterWindowProxy() : Base(0) {}
360 bool finalizeInBackground(const JS::Value& priv) const override {
361 return false;
364 // Standard internal methods
366 * Implementation of [[GetOwnProperty]] as defined at
367 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
369 * "proxy" is the WindowProxy object involved. It may not be same-compartment
370 * with cx.
372 bool getOwnPropertyDescriptor(
373 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
374 JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
377 * Implementation of the same-origin case of
378 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty>.
380 bool definePropertySameOrigin(JSContext* cx, JS::Handle<JSObject*> proxy,
381 JS::Handle<jsid> id,
382 JS::Handle<JS::PropertyDescriptor> desc,
383 JS::ObjectOpResult& result) const override;
386 * Implementation of [[OwnPropertyKeys]] as defined at
388 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys
390 * "proxy" is the WindowProxy object involved. It may not be same-compartment
391 * with cx.
393 bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
394 JS::MutableHandleVector<jsid> props) const override;
396 * Implementation of [[Delete]] as defined at
397 * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
399 * "proxy" is the WindowProxy object involved. It may not be same-compartment
400 * with cx.
402 bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
403 JS::ObjectOpResult& result) const override;
406 * Implementaton of hook for superclass getPrototype() method.
408 JSObject* getSameOriginPrototype(JSContext* cx) const override;
411 * Implementation of [[HasProperty]] internal method as defined at
412 * https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
414 * "proxy" is the WindowProxy object involved. It may not be same-compartment
415 * with cx.
417 * Note that the HTML spec does not define an override for this internal
418 * method, so we just want the "normal object" behavior. We have to override
419 * it, because js::Wrapper also overrides, with "not normal" behavior.
421 bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
422 bool* bp) const override;
425 * Implementation of [[Get]] internal method as defined at
426 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get>.
428 * "proxy" is the WindowProxy object involved. It may or may not be
429 * same-compartment with "cx".
431 * "receiver" is the receiver ("this") for the get. It will be
432 * same-compartment with "cx".
434 * "vp" is the return value. It will be same-compartment with "cx".
436 bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
437 JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
438 JS::MutableHandle<JS::Value> vp) const override;
441 * Implementation of [[Set]] internal method as defined at
442 * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set>.
444 * "proxy" is the WindowProxy object involved. It may or may not be
445 * same-compartment with "cx".
447 * "v" is the value being set. It will be same-compartment with "cx".
449 * "receiver" is the receiver ("this") for the set. It will be
450 * same-compartment with "cx".
452 bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
453 JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
454 JS::ObjectOpResult& result) const override;
456 // SpiderMonkey extensions
458 * Implementation of SpiderMonkey extension which just checks whether this
459 * object has the property. Basically Object.getOwnPropertyDescriptor(obj,
460 * prop) !== undefined. but does not require reifying the descriptor.
462 * We have to override this because js::Wrapper overrides it, but we want
463 * different behavior from js::Wrapper.
465 * "proxy" is the WindowProxy object involved. It may not be same-compartment
466 * with cx.
468 bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
469 bool* bp) const override;
472 * Implementation of SpiderMonkey extension which is used as a fast path for
473 * enumerating.
475 * We have to override this because js::Wrapper overrides it, but we want
476 * different behavior from js::Wrapper.
478 * "proxy" is the WindowProxy object involved. It may not be same-compartment
479 * with cx.
481 bool getOwnEnumerablePropertyKeys(
482 JSContext* cx, JS::Handle<JSObject*> proxy,
483 JS::MutableHandleVector<jsid> props) const override;
486 * Hook used by SpiderMonkey to implement Object.prototype.toString.
488 const char* className(JSContext* cx,
489 JS::Handle<JSObject*> wrapper) const override;
491 void finalize(JSFreeOp* fop, JSObject* proxy) const override;
492 size_t objectMoved(JSObject* proxy, JSObject* old) const override;
494 bool isCallable(JSObject* obj) const override { return false; }
495 bool isConstructor(JSObject* obj) const override { return false; }
497 static const nsOuterWindowProxy singleton;
499 protected:
500 static nsGlobalWindowOuter* GetOuterWindow(JSObject* proxy) {
501 nsGlobalWindowOuter* outerWindow =
502 nsGlobalWindowOuter::FromSupports(static_cast<nsISupports*>(
503 js::GetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT).toPrivate()));
504 return outerWindow;
507 // False return value means we threw an exception. True return value
508 // but false "found" means we didn't have a subframe at that index.
509 bool GetSubframeWindow(JSContext* cx, JS::Handle<JSObject*> proxy,
510 JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp,
511 bool& found) const;
513 // Returns a non-null window only if id is an index and we have a
514 // window at that index.
515 already_AddRefed<nsPIDOMWindowOuter> GetSubframeWindow(
516 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const;
518 bool AppendIndexedPropertyNames(JSObject* proxy,
519 JS::MutableHandleVector<jsid> props) const;
521 using MaybeCrossOriginObjectMixins::EnsureHolder;
522 bool EnsureHolder(JSContext* cx, JS::Handle<JSObject*> proxy,
523 JS::MutableHandle<JSObject*> holder) const override;
526 const char* nsOuterWindowProxy::className(JSContext* cx,
527 JS::Handle<JSObject*> proxy) const {
528 MOZ_ASSERT(js::IsProxy(proxy));
530 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
531 return "Object";
534 return "Window";
537 void nsOuterWindowProxy::finalize(JSFreeOp* fop, JSObject* proxy) const {
538 nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy);
539 if (outerWindow) {
540 outerWindow->ClearWrapper(proxy);
541 BrowsingContext* bc = outerWindow->GetBrowsingContext();
542 if (bc) {
543 bc->ClearWindowProxy();
546 // Ideally we would use OnFinalize here, but it's possible that
547 // EnsureScriptEnvironment will later be called on the window, and we don't
548 // want to create a new script object in that case. Therefore, we need to
549 // write a non-null value that will reliably crash when dereferenced.
550 outerWindow->PoisonOuterWindowProxy(proxy);
555 * IsNonConfigurableReadonlyPrimitiveGlobalProp returns true for
556 * property names that fit the following criteria:
558 * 1) The ES spec defines a property with that name on globals.
559 * 2) The property is non-configurable.
560 * 3) The property is non-writable (readonly).
561 * 4) The value of the property is a primitive (so doesn't change
562 * observably on when navigation happens).
564 * Such properties can act as actual non-configurable properties on a
565 * WindowProxy, because they are not affected by navigation.
567 #ifndef RELEASE_OR_BETA
568 static bool IsNonConfigurableReadonlyPrimitiveGlobalProp(JSContext* cx,
569 JS::Handle<jsid> id) {
570 return id == GetJSIDByIndex(cx, XPCJSContext::IDX_NAN) ||
571 id == GetJSIDByIndex(cx, XPCJSContext::IDX_UNDEFINED) ||
572 id == GetJSIDByIndex(cx, XPCJSContext::IDX_INFINITY);
574 #endif
576 bool nsOuterWindowProxy::getOwnPropertyDescriptor(
577 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
578 JS::MutableHandle<JS::PropertyDescriptor> desc) const {
579 // First check for indexed access. This is
580 // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
581 // step 2, mostly.
582 bool found;
583 if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) {
584 return false;
586 if (found) {
587 // Step 2.4.
588 FillPropertyDescriptor(desc, proxy, true);
589 return true;
592 bool isSameOrigin = IsPlatformObjectSameOrigin(cx, proxy);
594 // If we did not find a subframe, we could still have an indexed property
595 // access. In that case we should throw a SecurityError in the cross-origin
596 // case.
597 if (!isSameOrigin && IsArrayIndex(GetArrayIndexFromId(id))) {
598 // Step 2.5.2.
599 return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("access"));
602 // Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an
603 // IsArrayIndex(GetArrayIndexFromId(id)) here. We'll never have a property on
604 // the Window whose name is an index, because our defineProperty doesn't pass
605 // those on to the Window.
607 // Step 3.
608 if (isSameOrigin) {
609 // Fall through to js::Wrapper.
610 { // Scope for JSAutoRealm while we are dealing with js::Wrapper.
611 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
612 // for now. That's what js::Wrapper expects, and since we're same-origin
613 // anyway this is not changing any security behavior.
614 JSAutoRealm ar(cx, proxy);
615 JS_MarkCrossZoneId(cx, id);
616 bool ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
617 if (!ok) {
618 return false;
621 #ifndef RELEASE_OR_BETA // To be turned on in bug 1496510.
622 if (!IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
623 desc.setConfigurable(true);
625 #endif
628 // Now wrap our descriptor back into the Realm that asked for it.
629 return JS_WrapPropertyDescriptor(cx, desc);
632 // Step 4.
633 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
634 return false;
637 // Step 5
638 if (desc.object()) {
639 return true;
642 // Step 6 -- check for named subframes.
643 if (JSID_IS_STRING(id)) {
644 nsAutoJSString name;
645 if (!name.init(cx, JSID_TO_STRING(id))) {
646 return false;
648 nsGlobalWindowOuter* win = GetOuterWindow(proxy);
649 if (RefPtr<BrowsingContext> childDOMWin = win->GetChildWindow(name)) {
650 JS::Rooted<JS::Value> childValue(cx);
651 if (!ToJSValue(cx, WindowProxyHolder(childDOMWin), &childValue)) {
652 return false;
654 FillPropertyDescriptor(desc, proxy, childValue,
655 /* readonly = */ true,
656 /* enumerable = */ false);
657 return true;
661 // And step 7.
662 return CrossOriginPropertyFallback(cx, proxy, id, desc);
665 bool nsOuterWindowProxy::definePropertySameOrigin(
666 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
667 JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const {
668 if (IsArrayIndex(GetArrayIndexFromId(id))) {
669 // Spec says to Reject whether this is a supported index or not,
670 // since we have no indexed setter or indexed creator. It is up
671 // to the caller to decide whether to throw a TypeError.
672 return result.failCantDefineWindowElement();
675 JS::ObjectOpResult ourResult;
676 bool ok = js::Wrapper::defineProperty(cx, proxy, id, desc, ourResult);
677 if (!ok) {
678 return false;
681 if (!ourResult.ok()) {
682 // It's possible that this failed because the page got the existing
683 // descriptor (which we force to claim to be configurable) and then tried to
684 // redefine the property with the descriptor it got but a different value.
685 // We want to allow this case to succeed, so check for it and if we're in
686 // that case try again but now with an attempt to define a non-configurable
687 // property.
688 if (!desc.hasConfigurable() || !desc.configurable()) {
689 // The incoming descriptor was not explicitly marked "configurable: true",
690 // so it failed for some other reason. Just propagate that reason out.
691 result = ourResult;
692 return true;
695 JS::Rooted<JS::PropertyDescriptor> existingDesc(cx);
696 ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, &existingDesc);
697 if (!ok) {
698 return false;
700 if (!existingDesc.object() || existingDesc.configurable()) {
701 // We have no existing property, or its descriptor is already configurable
702 // (on the Window itself, where things really can be non-configurable).
703 // So we failed for some other reason, which we should propagate out.
704 result = ourResult;
705 return true;
708 JS::Rooted<JS::PropertyDescriptor> updatedDesc(cx, desc);
709 updatedDesc.setConfigurable(false);
711 JS::ObjectOpResult ourNewResult;
712 ok = js::Wrapper::defineProperty(cx, proxy, id, updatedDesc, ourNewResult);
713 if (!ok) {
714 return false;
717 if (!ourNewResult.ok()) {
718 // Twiddling the configurable flag didn't help. Just return this failure
719 // out to the caller.
720 result = ourNewResult;
721 return true;
725 #ifndef RELEASE_OR_BETA // To be turned on in bug 1496510.
726 if (desc.hasConfigurable() && !desc.configurable() &&
727 !IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
728 // Give callers a way to detect that they failed to "really" define a
729 // non-configurable property.
730 result.failCantDefineWindowNonConfigurable();
731 return true;
733 #endif
735 result.succeed();
736 return true;
739 bool nsOuterWindowProxy::ownPropertyKeys(
740 JSContext* cx, JS::Handle<JSObject*> proxy,
741 JS::MutableHandleVector<jsid> props) const {
742 // Just our indexed stuff followed by our "normal" own property names.
743 if (!AppendIndexedPropertyNames(proxy, props)) {
744 return false;
747 if (IsPlatformObjectSameOrigin(cx, proxy)) {
748 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
749 // for now. That's what js::Wrapper expects, and since we're same-origin
750 // anyway this is not changing any security behavior.
751 JS::RootedVector<jsid> innerProps(cx);
752 { // Scope for JSAutoRealm so we can mark the ids once we exit it
753 JSAutoRealm ar(cx, proxy);
754 if (!js::Wrapper::ownPropertyKeys(cx, proxy, &innerProps)) {
755 return false;
758 for (auto& id : innerProps) {
759 JS_MarkCrossZoneId(cx, id);
761 return js::AppendUnique(cx, props, innerProps);
764 // In the cross-origin case we purposefully exclude subframe names from the
765 // list of property names we report here.
766 JS::Rooted<JSObject*> holder(cx);
767 if (!EnsureHolder(cx, proxy, &holder)) {
768 return false;
771 JS::RootedVector<jsid> crossOriginProps(cx);
772 if (!js::GetPropertyKeys(cx, holder,
773 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
774 &crossOriginProps) ||
775 !js::AppendUnique(cx, props, crossOriginProps)) {
776 return false;
779 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
782 bool nsOuterWindowProxy::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
783 JS::Handle<jsid> id,
784 JS::ObjectOpResult& result) const {
785 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
786 return ReportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("delete"));
789 if (nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
790 // Fail (which means throw if strict, else return false).
791 return result.failCantDeleteWindowElement();
794 if (IsArrayIndex(GetArrayIndexFromId(id))) {
795 // Indexed, but not supported. Spec says return true.
796 return result.succeed();
799 // We're same-origin, so it should be safe to enter the Realm of "proxy".
800 // Let's do that, just in case, to avoid cross-compartment issues in our
801 // js::Wrapper caller..
802 JSAutoRealm ar(cx, proxy);
803 JS_MarkCrossZoneId(cx, id);
804 return js::Wrapper::delete_(cx, proxy, id, result);
807 JSObject* nsOuterWindowProxy::getSameOriginPrototype(JSContext* cx) const {
808 return Window_Binding::GetProtoObjectHandle(cx);
811 bool nsOuterWindowProxy::has(JSContext* cx, JS::Handle<JSObject*> proxy,
812 JS::Handle<jsid> id, bool* bp) const {
813 // We could just directly forward this method to js::BaseProxyHandler, but
814 // that involves reifying the actual property descriptor, which might be more
815 // work than we have to do for has() on the Window.
817 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
818 // In the cross-origin case we only have own properties. Just call hasOwn
819 // directly.
820 return hasOwn(cx, proxy, id, bp);
823 if (nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
824 *bp = true;
825 return true;
828 // Just to be safe in terms of compartment asserts, enter the Realm of
829 // "proxy". We're same-origin with it, so this should be safe.
830 JSAutoRealm ar(cx, proxy);
831 JS_MarkCrossZoneId(cx, id);
832 return js::Wrapper::has(cx, proxy, id, bp);
835 bool nsOuterWindowProxy::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
836 JS::Handle<jsid> id, bool* bp) const {
837 // We could just directly forward this method to js::BaseProxyHandler, but
838 // that involves reifying the actual property descriptor, which might be more
839 // work than we have to do for hasOwn() on the Window.
841 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
842 // Avoiding reifying the property descriptor here would require duplicating
843 // a bunch of "is this property exposed cross-origin" logic, which is
844 // probably not worth it. Just forward this along to the base
845 // implementation.
847 // It's very important to not forward this to js::Wrapper, because that will
848 // not do the right security and cross-origin checks and will pass through
849 // the call to the Window.
851 // The BaseProxyHandler code is OK with this happening without entering the
852 // compartment of "proxy".
853 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
856 if (nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
857 *bp = true;
858 return true;
861 // Just to be safe in terms of compartment asserts, enter the Realm of
862 // "proxy". We're same-origin with it, so this should be safe.
863 JSAutoRealm ar(cx, proxy);
864 JS_MarkCrossZoneId(cx, id);
865 return js::Wrapper::hasOwn(cx, proxy, id, bp);
868 bool nsOuterWindowProxy::get(JSContext* cx, JS::Handle<JSObject*> proxy,
869 JS::Handle<JS::Value> receiver,
870 JS::Handle<jsid> id,
871 JS::MutableHandle<JS::Value> vp) const {
872 if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
873 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
874 vp.set(JS::ObjectValue(*proxy));
875 return MaybeWrapValue(cx, vp);
878 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
879 return CrossOriginGet(cx, proxy, receiver, id, vp);
882 bool found;
883 if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
884 return false;
887 if (found) {
888 return true;
891 { // Scope for JSAutoRealm
892 // Enter "proxy"'s Realm. We're in the same-origin case, so this should be
893 // safe.
894 JSAutoRealm ar(cx, proxy);
896 JS_MarkCrossZoneId(cx, id);
898 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
899 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
900 return false;
903 // Fall through to js::Wrapper.
904 if (!js::Wrapper::get(cx, proxy, wrappedReceiver, id, vp)) {
905 return false;
909 // Make sure our return value is in the caller compartment.
910 return MaybeWrapValue(cx, vp);
913 bool nsOuterWindowProxy::set(JSContext* cx, JS::Handle<JSObject*> proxy,
914 JS::Handle<jsid> id, JS::Handle<JS::Value> v,
915 JS::Handle<JS::Value> receiver,
916 JS::ObjectOpResult& result) const {
917 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
918 return CrossOriginSet(cx, proxy, id, v, receiver, result);
921 if (IsArrayIndex(GetArrayIndexFromId(id))) {
922 // Reject the set. It's up to the caller to decide whether to throw a
923 // TypeError. If the caller is strict mode JS code, it'll throw.
924 return result.failReadOnly();
927 // Do the rest in the Realm of "proxy", since we're in the same-origin case.
928 JSAutoRealm ar(cx, proxy);
929 JS::Rooted<JS::Value> wrappedArg(cx, v);
930 if (!MaybeWrapValue(cx, &wrappedArg)) {
931 return false;
933 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
934 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
935 return false;
938 JS_MarkCrossZoneId(cx, id);
940 return js::Wrapper::set(cx, proxy, id, wrappedArg, wrappedReceiver, result);
943 bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
944 JSContext* cx, JS::Handle<JSObject*> proxy,
945 JS::MutableHandleVector<jsid> props) const {
946 // We could just stop overring getOwnEnumerablePropertyKeys and let our
947 // superclasses deal (by falling back on the BaseProxyHandler implementation
948 // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to
949 // only return the enumerable ones. But maybe there's value in having
950 // somewhat faster for-in iteration on Window objects...
952 // Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable
953 // own property names.
954 if (!AppendIndexedPropertyNames(proxy, props)) {
955 return false;
958 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
959 // All the cross-origin properties other than the indexed props are
960 // non-enumerable, so we're done here.
961 return true;
964 // When forwarding to js::Wrapper, we should just enter the Realm of proxy
965 // for now. That's what js::Wrapper expects, and since we're same-origin
966 // anyway this is not changing any security behavior.
967 JS::RootedVector<jsid> innerProps(cx);
968 { // Scope for JSAutoRealm so we can mark the ids once we exit it.
969 JSAutoRealm ar(cx, proxy);
970 if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx, proxy, &innerProps)) {
971 return false;
975 for (auto& id : innerProps) {
976 JS_MarkCrossZoneId(cx, id);
979 return js::AppendUnique(cx, props, innerProps);
982 bool nsOuterWindowProxy::GetSubframeWindow(JSContext* cx,
983 JS::Handle<JSObject*> proxy,
984 JS::Handle<jsid> id,
985 JS::MutableHandle<JS::Value> vp,
986 bool& found) const {
987 nsCOMPtr<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id);
988 if (!frame) {
989 found = false;
990 return true;
993 found = true;
994 // Just return the window's global
995 nsGlobalWindowOuter* global = nsGlobalWindowOuter::Cast(frame);
996 frame->EnsureInnerWindow();
997 JSObject* obj = global->GetGlobalJSObject();
998 // This null check fixes a hard-to-reproduce crash that occurs when we
999 // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
1000 // comment 105.
1001 if (MOZ_UNLIKELY(!obj)) {
1002 return xpc::Throw(cx, NS_ERROR_FAILURE);
1004 vp.setObject(*obj);
1005 return JS_WrapValue(cx, vp);
1008 already_AddRefed<nsPIDOMWindowOuter> nsOuterWindowProxy::GetSubframeWindow(
1009 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const {
1010 uint32_t index = GetArrayIndexFromId(id);
1011 if (!IsArrayIndex(index)) {
1012 return nullptr;
1015 nsGlobalWindowOuter* win = GetOuterWindow(proxy);
1016 return win->IndexedGetterOuter(index);
1019 bool nsOuterWindowProxy::AppendIndexedPropertyNames(
1020 JSObject* proxy, JS::MutableHandleVector<jsid> props) const {
1021 uint32_t length = GetOuterWindow(proxy)->Length();
1022 MOZ_ASSERT(int32_t(length) >= 0);
1023 if (!props.reserve(props.length() + length)) {
1024 return false;
1026 for (int32_t i = 0; i < int32_t(length); ++i) {
1027 if (!props.append(INT_TO_JSID(i))) {
1028 return false;
1032 return true;
1035 bool nsOuterWindowProxy::EnsureHolder(
1036 JSContext* cx, JS::Handle<JSObject*> proxy,
1037 JS::MutableHandle<JSObject*> holder) const {
1038 return EnsureHolder(cx, proxy, HOLDER_WEAKMAP_SLOT,
1039 Window_Binding::sCrossOriginAttributes,
1040 Window_Binding::sCrossOriginMethods, holder);
1043 size_t nsOuterWindowProxy::objectMoved(JSObject* obj, JSObject* old) const {
1044 nsGlobalWindowOuter* outerWindow = GetOuterWindow(obj);
1045 if (outerWindow) {
1046 outerWindow->UpdateWrapper(obj, old);
1047 BrowsingContext* bc = outerWindow->GetBrowsingContext();
1048 if (bc) {
1049 bc->UpdateWindowProxy(obj, old);
1052 return 0;
1055 const nsOuterWindowProxy nsOuterWindowProxy::singleton;
1057 class nsChromeOuterWindowProxy : public nsOuterWindowProxy {
1058 public:
1059 constexpr nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
1061 const char* className(JSContext* cx,
1062 JS::Handle<JSObject*> wrapper) const override;
1064 static const nsChromeOuterWindowProxy singleton;
1067 const char* nsChromeOuterWindowProxy::className(
1068 JSContext* cx, JS::Handle<JSObject*> proxy) const {
1069 MOZ_ASSERT(js::IsProxy(proxy));
1071 return "ChromeWindow";
1074 const nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton;
1076 static JSObject* NewOuterWindowProxy(JSContext* cx,
1077 JS::Handle<JSObject*> global,
1078 bool isChrome) {
1079 MOZ_ASSERT(JS_IsGlobalObject(global));
1081 JSAutoRealm ar(cx, global);
1083 js::WrapperOptions options;
1084 options.setClass(&OuterWindowProxyClass);
1085 options.setSingleton(true);
1086 JSObject* obj =
1087 js::Wrapper::New(cx, global,
1088 isChrome ? &nsChromeOuterWindowProxy::singleton
1089 : &nsOuterWindowProxy::singleton,
1090 options);
1091 MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
1092 return obj;
1095 //*****************************************************************************
1096 //*** nsGlobalWindowOuter: Object Management
1097 //*****************************************************************************
1099 nsGlobalWindowOuter::nsGlobalWindowOuter(uint64_t aWindowID)
1100 : nsPIDOMWindowOuter(aWindowID),
1101 mFullscreen(false),
1102 mFullscreenMode(false),
1103 mIsClosed(false),
1104 mInClose(false),
1105 mHavePendingClose(false),
1106 mHadOriginalOpener(false),
1107 mIsPopupSpam(false),
1108 mBlockScriptedClosingFlag(false),
1109 mWasOffline(false),
1110 mCreatingInnerWindow(false),
1111 mIsChrome(false),
1112 mAllowScriptsToClose(false),
1113 mTopLevelOuterContentWindow(false),
1114 mHasStorageAccess(false),
1115 #ifdef DEBUG
1116 mSerial(0),
1117 mSetOpenerWindowCalled(false),
1118 #endif
1119 mCleanedUp(false),
1120 #ifdef DEBUG
1121 mIsValidatingTabGroup(false),
1122 #endif
1123 mCanSkipCCGeneration(0),
1124 mAutoActivateVRDisplayID(0) {
1125 AssertIsOnMainThread();
1127 nsLayoutStatics::AddRef();
1129 // Initialize the PRCList (this).
1130 PR_INIT_CLIST(this);
1132 // |this| is an outer window. Outer windows start out frozen and
1133 // remain frozen until they get an inner window.
1134 MOZ_ASSERT(IsFrozen());
1136 // We could have failed the first time through trying
1137 // to create the entropy collector, so we should
1138 // try to get one until we succeed.
1140 #ifdef DEBUG
1141 mSerial = nsContentUtils::InnerOrOuterWindowCreated();
1143 if (!PR_GetEnv("MOZ_QUIET")) {
1144 printf_stderr(
1145 "++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1146 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1147 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1148 nullptr);
1150 #endif
1152 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
1153 ("DOMWINDOW %p created outer=nullptr", this));
1155 // Add ourselves to the outer windows list.
1156 MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!");
1158 // |this| is an outer window, add to the outer windows list.
1159 MOZ_ASSERT(!sOuterWindowsById->Get(mWindowID),
1160 "This window shouldn't be in the hash table yet!");
1161 // We seem to see crashes in release builds because of null
1162 // |sOuterWindowsById|.
1163 if (sOuterWindowsById) {
1164 sOuterWindowsById->Put(mWindowID, this);
1168 #ifdef DEBUG
1170 /* static */
1171 void nsGlobalWindowOuter::AssertIsOnMainThread() {
1172 MOZ_ASSERT(NS_IsMainThread());
1175 #endif // DEBUG
1177 /* static */
1178 void nsGlobalWindowOuter::Init() {
1179 AssertIsOnMainThread();
1181 NS_ASSERTION(gDOMLeakPRLogOuter,
1182 "gDOMLeakPRLogOuter should have been initialized!");
1184 sOuterWindowsById = new OuterWindowByIdTable();
1187 nsGlobalWindowOuter::~nsGlobalWindowOuter() {
1188 AssertIsOnMainThread();
1190 if (sOuterWindowsById) {
1191 MOZ_ASSERT(sOuterWindowsById->Get(mWindowID),
1192 "This window should be in the hash table");
1193 sOuterWindowsById->Remove(mWindowID);
1196 nsContentUtils::InnerOrOuterWindowDestroyed();
1198 #ifdef DEBUG
1199 if (!PR_GetEnv("MOZ_QUIET")) {
1200 nsAutoCString url;
1201 if (mLastOpenedURI) {
1202 url = mLastOpenedURI->GetSpecOrDefault();
1204 // Data URLs can be very long, so truncate to avoid flooding the log.
1205 const uint32_t maxURLLength = 1000;
1206 if (url.Length() > maxURLLength) {
1207 url.Truncate(maxURLLength);
1211 printf_stderr(
1212 "--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = "
1213 "%s]\n",
1214 nsContentUtils::GetCurrentInnerOrOuterWindowCount(),
1215 static_cast<void*>(ToCanonicalSupports(this)), getpid(), mSerial,
1216 nullptr, url.get());
1218 #endif
1220 MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
1221 ("DOMWINDOW %p destroyed", this));
1223 JSObject* proxy = GetWrapperMaybeDead();
1224 if (proxy) {
1225 if (mBrowsingContext) {
1226 mBrowsingContext->ClearWindowProxy();
1228 js::SetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT,
1229 js::PrivateValue(nullptr));
1232 // An outer window is destroyed with inner windows still possibly
1233 // alive, iterate through the inner windows and null out their
1234 // back pointer to this outer, and pull them out of the list of
1235 // inner windows.
1237 // Our linked list of inner windows both contains (an nsGlobalWindowOuter),
1238 // and our inner windows (nsGlobalWindowInners). This means that we need to
1239 // use PRCList*. We can then compare that PRCList* to `this` to see if its an
1240 // inner or outer window.
1241 PRCList* w;
1242 while ((w = PR_LIST_HEAD(this)) != this) {
1243 PR_REMOVE_AND_INIT_LINK(w);
1246 DropOuterWindowDocs();
1248 if (mTabGroup) {
1249 mTabGroup->Leave(this);
1252 // Outer windows are always supposed to call CleanUp before letting themselves
1253 // be destroyed.
1254 MOZ_ASSERT(mCleanedUp);
1256 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1257 if (ac) ac->RemoveWindowAsListener(this);
1259 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1260 if (obs) {
1261 obs->RemoveObserver(this, PERM_CHANGE_NOTIFICATION);
1264 nsLayoutStatics::Release();
1267 // static
1268 void nsGlobalWindowOuter::ShutDown() {
1269 AssertIsOnMainThread();
1271 delete sOuterWindowsById;
1272 sOuterWindowsById = nullptr;
1275 void nsGlobalWindowOuter::MaybeForgiveSpamCount() {
1276 if (IsPopupSpamWindow()) {
1277 SetIsPopupSpamWindow(false);
1281 void nsGlobalWindowOuter::SetIsPopupSpamWindow(bool aIsPopupSpam) {
1282 mIsPopupSpam = aIsPopupSpam;
1283 if (aIsPopupSpam) {
1284 ++gOpenPopupSpamCount;
1285 } else {
1286 --gOpenPopupSpamCount;
1287 NS_ASSERTION(gOpenPopupSpamCount >= 0,
1288 "Unbalanced decrement of gOpenPopupSpamCount");
1292 void nsGlobalWindowOuter::DropOuterWindowDocs() {
1293 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
1294 mDoc = nullptr;
1295 mSuspendedDoc = nullptr;
1298 void nsGlobalWindowOuter::CleanUp() {
1299 // Guarantee idempotence.
1300 if (mCleanedUp) return;
1301 mCleanedUp = true;
1303 StartDying();
1305 mFrames = nullptr;
1306 mWindowUtils = nullptr;
1308 ClearControllers();
1310 mOpener = nullptr; // Forces Release
1311 if (mContext) {
1312 mContext = nullptr; // Forces Release
1314 mChromeEventHandler = nullptr; // Forces Release
1315 mParentTarget = nullptr;
1316 mMessageManager = nullptr;
1318 mArguments = nullptr;
1321 void nsGlobalWindowOuter::ClearControllers() {
1322 if (mControllers) {
1323 uint32_t count;
1324 mControllers->GetControllerCount(&count);
1326 while (count--) {
1327 nsCOMPtr<nsIController> controller;
1328 mControllers->GetControllerAt(count, getter_AddRefs(controller));
1330 nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
1331 if (context) context->SetCommandContext(nullptr);
1334 mControllers = nullptr;
1338 //*****************************************************************************
1339 // nsGlobalWindowOuter::nsISupports
1340 //*****************************************************************************
1342 // QueryInterface implementation for nsGlobalWindowOuter
1343 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowOuter)
1344 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1345 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
1346 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1347 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1348 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1349 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1350 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1351 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindowOuter)
1352 NS_INTERFACE_MAP_ENTRY(mozIDOMWindowProxy)
1353 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMChromeWindow, IsChromeWindow())
1354 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1355 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1356 NS_INTERFACE_MAP_ENTRY(nsIObserver)
1357 NS_INTERFACE_MAP_END
1359 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindowOuter)
1360 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindowOuter)
1362 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindowOuter)
1363 if (tmp->IsBlackForCC(false)) {
1364 if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) {
1365 return true;
1367 tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration;
1368 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1369 elm->MarkForCC();
1371 return true;
1373 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1375 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindowOuter)
1376 return tmp->IsBlackForCC(true);
1377 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1379 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindowOuter)
1380 return tmp->IsBlackForCC(false);
1381 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1383 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindowOuter)
1385 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowOuter)
1386 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1387 char name[512];
1388 nsAutoCString uri;
1389 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1390 uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
1392 SprintfLiteral(name, "nsGlobalWindowOuter # %" PRIu64 " outer %s",
1393 tmp->mWindowID, uri.get());
1394 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1395 } else {
1396 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowOuter, tmp->mRefCnt.get())
1399 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
1401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
1404 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
1406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentStoragePrincipal)
1408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1410 // Traverse stuff from nsPIDOMWindow
1411 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1412 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
1414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
1415 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerForInitialContentBrowser)
1417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
1418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
1420 tmp->TraverseHostObjectURIs(cb);
1422 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
1423 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1425 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowOuter)
1426 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
1428 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1429 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
1431 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1432 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
1433 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1434 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentStoragePrincipal)
1435 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1437 // Unlink stuff from nsPIDOMWindow
1438 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1439 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1440 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
1441 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
1442 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerForInitialContentBrowser)
1444 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
1445 if (tmp->mBrowsingContext) {
1446 tmp->mBrowsingContext->ClearWindowProxy();
1447 tmp->mBrowsingContext = nullptr;
1450 tmp->UnlinkHostObjectURIs();
1452 if (tmp->IsChromeWindow()) {
1453 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
1456 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1457 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1459 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindowOuter)
1460 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1461 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1463 bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded) {
1464 if (!nsCCUncollectableMarker::sGeneration) {
1465 return false;
1468 // Unlike most wrappers, the outer window wrapper is not a wrapper for
1469 // the outer window. Instead, the outer window wrapper holds the inner
1470 // window binding object, which in turn holds the nsGlobalWindowInner, which
1471 // has a strong reference to the nsGlobalWindowOuter. We're using the
1472 // mInnerWindow pointer as a flag for that whole chain.
1473 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1474 (mInnerWindow && HasKnownLiveWrapper())) &&
1475 (!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
1478 //*****************************************************************************
1479 // nsGlobalWindowOuter::nsIScriptGlobalObject
1480 //*****************************************************************************
1482 nsresult nsGlobalWindowOuter::EnsureScriptEnvironment() {
1483 if (GetWrapperPreserveColor()) {
1484 return NS_OK;
1487 NS_ASSERTION(!GetCurrentInnerWindowInternal(),
1488 "No cached wrapper, but we have an inner window?");
1490 // If this window is a [i]frame, don't bother GC'ing when the frame's context
1491 // is destroyed since a GC will happen when the frameset or host document is
1492 // destroyed anyway.
1493 nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), this);
1495 NS_ASSERTION(!mContext, "Will overwrite mContext!");
1497 // should probably assert the context is clean???
1498 context->WillInitializeContext();
1500 nsresult rv = context->InitContext();
1501 NS_ENSURE_SUCCESS(rv, rv);
1503 mContext = context;
1504 return NS_OK;
1507 nsIScriptContext* nsGlobalWindowOuter::GetScriptContext() { return mContext; }
1509 bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* aNewDocument) {
1510 // We reuse the inner window when:
1511 // a. We are currently at our original document.
1512 // b. At least one of the following conditions are true:
1513 // -- The new document is the same as the old document. This means that we're
1514 // getting called from document.open().
1515 // -- The new document has the same origin as what we have loaded right now.
1517 if (!mDoc || !aNewDocument) {
1518 return false;
1521 if (!mDoc->IsInitialDocument()) {
1522 return false;
1525 #ifdef DEBUG
1527 nsCOMPtr<nsIURI> uri;
1528 NS_GetURIWithoutRef(mDoc->GetDocumentURI(), getter_AddRefs(uri));
1529 NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
1531 #endif
1533 // Great, we're the original document, check for one of the other
1534 // conditions.
1536 if (mDoc == aNewDocument) {
1537 return true;
1540 bool equal;
1541 if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(),
1542 &equal)) &&
1543 equal) {
1544 // The origin is the same.
1545 return true;
1548 return false;
1551 void nsGlobalWindowOuter::SetInitialPrincipalToSubject(
1552 nsIContentSecurityPolicy* aCSP) {
1553 // First, grab the subject principal.
1554 nsCOMPtr<nsIPrincipal> newWindowPrincipal =
1555 nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
1557 // We should never create windows with an expanded principal.
1558 // If we have a system principal, make sure we're not using it for a content
1559 // docshell.
1560 // NOTE: Please keep this logic in sync with nsWebShellWindow::Initialize().
1561 if (nsContentUtils::IsExpandedPrincipal(newWindowPrincipal) ||
1562 (nsContentUtils::IsSystemPrincipal(newWindowPrincipal) &&
1563 GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome)) {
1564 newWindowPrincipal = nullptr;
1567 // If there's an existing document, bail if it either:
1568 if (mDoc) {
1569 // (a) is not an initial about:blank document, or
1570 if (!mDoc->IsInitialDocument()) return;
1571 // (b) already has the correct principal.
1572 if (mDoc->NodePrincipal() == newWindowPrincipal) return;
1574 #ifdef DEBUG
1575 // If we have a document loaded at this point, it had better be about:blank.
1576 // Otherwise, something is really weird. An about:blank page has a
1577 // NullPrincipal.
1578 bool isNullPrincipal;
1579 MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(
1580 &isNullPrincipal)) &&
1581 isNullPrincipal);
1582 #endif
1585 GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal, aCSP);
1587 if (mDoc) {
1588 mDoc->SetIsInitialDocument(true);
1591 RefPtr<PresShell> presShell = GetDocShell()->GetPresShell();
1592 if (presShell && !presShell->DidInitialize()) {
1593 // Ensure that if someone plays with this document they will get
1594 // layout happening.
1595 presShell->Initialize();
1599 #define WINDOWSTATEHOLDER_IID \
1601 0x0b917c3e, 0xbd50, 0x4683, { \
1602 0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26 \
1606 class WindowStateHolder final : public nsISupports {
1607 public:
1608 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
1609 NS_DECL_ISUPPORTS
1611 explicit WindowStateHolder(nsGlobalWindowInner* aWindow);
1613 nsGlobalWindowInner* GetInnerWindow() { return mInnerWindow; }
1615 void DidRestoreWindow() {
1616 mInnerWindow = nullptr;
1617 mInnerWindowReflector = nullptr;
1620 protected:
1621 ~WindowStateHolder();
1623 nsGlobalWindowInner* mInnerWindow;
1624 // We hold onto this to make sure the inner window doesn't go away. The outer
1625 // window ends up recalculating it anyway.
1626 JS::PersistentRooted<JSObject*> mInnerWindowReflector;
1629 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
1631 WindowStateHolder::WindowStateHolder(nsGlobalWindowInner* aWindow)
1632 : mInnerWindow(aWindow),
1633 mInnerWindowReflector(RootingCx(), aWindow->GetWrapper()) {
1634 MOZ_ASSERT(aWindow, "null window");
1636 aWindow->Suspend();
1638 // When a global goes into the bfcache, we disable script.
1639 xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false);
1642 WindowStateHolder::~WindowStateHolder() {
1643 if (mInnerWindow) {
1644 // This window was left in the bfcache and is now going away. We need to
1645 // free it up.
1646 // Note that FreeInnerObjects may already have been called on the
1647 // inner window if its outer has already had SetDocShell(null)
1648 // called.
1649 mInnerWindow->FreeInnerObjects();
1653 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
1655 bool nsGlobalWindowOuter::ComputeIsSecureContext(Document* aDocument,
1656 SecureContextFlags aFlags) {
1657 nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
1658 if (nsContentUtils::IsSystemPrincipal(principal)) {
1659 return true;
1662 // Implement https://w3c.github.io/webappsec-secure-contexts/#settings-object
1663 // With some modifications to allow for aFlags.
1665 bool hadNonSecureContextCreator = false;
1667 nsPIDOMWindowOuter* parentOuterWin = GetScriptableParent();
1668 MOZ_ASSERT(parentOuterWin, "How can we get here? No docShell somehow?");
1669 if (nsGlobalWindowOuter::Cast(parentOuterWin) != this) {
1670 // There may be a small chance that parentOuterWin has navigated in
1671 // the time that it took us to start loading this sub-document. If that
1672 // were the case then parentOuterWin->GetCurrentInnerWindow() wouldn't
1673 // return the window for the document that is embedding us. For this
1674 // reason we only use the GetScriptableParent call above to check that we
1675 // have a same-type parent, but actually get the inner window via the
1676 // document that we know is embedding us.
1677 Document* creatorDoc = aDocument->GetParentDocument();
1678 if (!creatorDoc) {
1679 return false; // we must be tearing down
1681 nsGlobalWindowInner* parentWin =
1682 nsGlobalWindowInner::Cast(creatorDoc->GetInnerWindow());
1683 if (!parentWin) {
1684 return false; // we must be tearing down
1686 MOZ_ASSERT(parentWin == nsGlobalWindowInner::Cast(
1687 parentOuterWin->GetCurrentInnerWindow()),
1688 "Creator window mismatch while setting Secure Context state");
1689 hadNonSecureContextCreator = !parentWin->IsSecureContext();
1692 if (hadNonSecureContextCreator) {
1693 return false;
1696 if (nsContentUtils::HttpsStateIsModern(aDocument)) {
1697 return true;
1700 if (principal->GetIsNullPrincipal()) {
1701 nsCOMPtr<nsIURI> uri = aDocument->GetOriginalURI();
1702 // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so
1703 // it doesn't actually matter what we use here, but reusing the document
1704 // principal's attributes is convenient.
1705 const OriginAttributes& attrs = principal->OriginAttributesRef();
1706 // CreateCodebasePrincipal correctly gets a useful principal for blob: and
1707 // other URI_INHERITS_SECURITY_CONTEXT URIs.
1708 principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
1709 if (NS_WARN_IF(!principal)) {
1710 return false;
1714 nsCOMPtr<nsIContentSecurityManager> csm =
1715 do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
1716 NS_WARNING_ASSERTION(csm, "csm is null");
1717 if (csm) {
1718 bool isTrustworthyOrigin = false;
1719 csm->IsOriginPotentiallyTrustworthy(principal, &isTrustworthyOrigin);
1720 if (isTrustworthyOrigin) {
1721 return true;
1725 return false;
1728 // We need certain special behavior for remote XUL whitelisted domains, but we
1729 // don't want that behavior to take effect in automation, because we whitelist
1730 // all the mochitest domains. So we need to check a pref here.
1731 static bool TreatAsRemoteXUL(nsIPrincipal* aPrincipal) {
1732 MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(aPrincipal));
1733 return nsContentUtils::AllowXULXBLForPrincipal(aPrincipal) &&
1734 !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
1737 static bool EnablePrivilege(JSContext* cx, unsigned argc, JS::Value* vp) {
1738 Telemetry::Accumulate(Telemetry::ENABLE_PRIVILEGE_EVER_CALLED, true);
1739 return xpc::EnableUniversalXPConnect(cx);
1742 static const JSFunctionSpec EnablePrivilegeSpec[] = {
1743 JS_FN("enablePrivilege", EnablePrivilege, 1, 0), JS_FS_END};
1745 static bool InitializeLegacyNetscapeObject(JSContext* aCx,
1746 JS::Handle<JSObject*> aGlobal) {
1747 JSAutoRealm ar(aCx, aGlobal);
1749 // Note: MathJax depends on window.netscape being exposed. See bug 791526.
1750 JS::Rooted<JSObject*> obj(aCx);
1751 obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr);
1752 NS_ENSURE_TRUE(obj, false);
1754 obj = JS_DefineObject(aCx, obj, "security", nullptr);
1755 NS_ENSURE_TRUE(obj, false);
1757 // We hide enablePrivilege behind a pref because it has been altered in a
1758 // way that makes it fundamentally insecure to use in production. Mozilla
1759 // uses this pref during automated testing to support legacy test code that
1760 // uses enablePrivilege. If you're not doing test automation, you _must_ not
1761 // flip this pref, or you will be exposing all your users to security
1762 // vulnerabilities.
1763 if (!xpc::IsInAutomation()) {
1764 return true;
1767 /* Define PrivilegeManager object with the necessary "static" methods. */
1768 obj = JS_DefineObject(aCx, obj, "PrivilegeManager", nullptr);
1769 NS_ENSURE_TRUE(obj, false);
1771 return JS_DefineFunctions(aCx, obj, EnablePrivilegeSpec);
1774 struct MOZ_STACK_CLASS CompartmentFinderState {
1775 explicit CompartmentFinderState(nsIPrincipal* aPrincipal)
1776 : principal(aPrincipal), compartment(nullptr) {}
1778 // Input: we look for a compartment which is same-origin with the
1779 // given principal.
1780 nsIPrincipal* principal;
1782 // Output: We set this member if we find a compartment.
1783 JS::Compartment* compartment;
1786 static JS::CompartmentIterResult FindSameOriginCompartment(
1787 JSContext* aCx, void* aData, JS::Compartment* aCompartment) {
1788 auto* data = static_cast<CompartmentFinderState*>(aData);
1789 MOZ_ASSERT(!data->compartment, "Why are we getting called?");
1791 // If this compartment is not safe to share across globals, don't do
1792 // anything with it; in particular we should not be getting a
1793 // CompartmentPrivate from such a compartment, because it may be in
1794 // the middle of being collected and its CompartmentPrivate may no
1795 // longer be valid.
1796 if (!js::IsSharableCompartment(aCompartment)) {
1797 return JS::CompartmentIterResult::KeepGoing;
1800 auto* compartmentPrivate = xpc::CompartmentPrivate::Get(aCompartment);
1801 if (!compartmentPrivate->CanShareCompartmentWith(data->principal)) {
1802 // Can't reuse this one, keep going.
1803 return JS::CompartmentIterResult::KeepGoing;
1806 // We have a winner!
1807 data->compartment = aCompartment;
1808 return JS::CompartmentIterResult::Stop;
1811 static JS::RealmCreationOptions& SelectZone(
1812 JSContext* aCx, nsIPrincipal* aPrincipal, nsGlobalWindowInner* aNewInner,
1813 JS::RealmCreationOptions& aOptions) {
1814 // Use the shared system compartment for chrome windows.
1815 if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
1816 return aOptions.setExistingCompartment(xpc::PrivilegedJunkScope());
1819 if (aNewInner->GetOuterWindow()) {
1820 nsGlobalWindowOuter* top = aNewInner->GetTopInternal();
1821 if (top == aNewInner->GetOuterWindow()) {
1822 // We're a toplevel load. Use a new zone. This way, when we do
1823 // zone-based compartment sharing we won't share compartments
1824 // across navigations.
1825 return aOptions.setNewCompartmentAndZone();
1828 // If we have a top-level window, use its zone.
1829 if (top && top->GetGlobalJSObject()) {
1830 JS::Zone* zone = JS::GetObjectZone(top->GetGlobalJSObject());
1831 // Now try to find an existing compartment that's same-origin
1832 // with our principal.
1833 CompartmentFinderState data(aPrincipal);
1834 JS_IterateCompartmentsInZone(aCx, zone, &data, FindSameOriginCompartment);
1835 if (data.compartment) {
1836 return aOptions.setExistingCompartment(data.compartment);
1838 return aOptions.setNewCompartmentInExistingZone(top->GetGlobalJSObject());
1842 return aOptions.setNewCompartmentAndZone();
1846 * Create a new global object that will be used for an inner window.
1847 * Return the native global and an nsISupports 'holder' that can be used
1848 * to manage the lifetime of it.
1850 static nsresult CreateNativeGlobalForInner(JSContext* aCx,
1851 nsGlobalWindowInner* aNewInner,
1852 nsIURI* aURI,
1853 nsIPrincipal* aPrincipal,
1854 JS::MutableHandle<JSObject*> aGlobal,
1855 bool aIsSecureContext) {
1856 MOZ_ASSERT(aCx);
1857 MOZ_ASSERT(aNewInner);
1858 MOZ_ASSERT(aPrincipal);
1860 // DOMWindow with nsEP is not supported, we have to make sure
1861 // no one creates one accidentally.
1862 nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal);
1863 MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
1865 JS::RealmOptions options;
1867 SelectZone(aCx, aPrincipal, aNewInner, options.creationOptions());
1869 options.creationOptions().setSecureContext(aIsSecureContext);
1871 xpc::InitGlobalObjectOptions(options, aPrincipal);
1873 // Determine if we need the Components object.
1874 bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
1875 TreatAsRemoteXUL(aPrincipal);
1876 uint32_t flags = needComponents ? 0 : xpc::OMIT_COMPONENTS_OBJECT;
1877 flags |= xpc::DONT_FIRE_ONNEWGLOBALHOOK;
1879 if (!Window_Binding::Wrap(aCx, aNewInner, aNewInner, options,
1880 nsJSPrincipals::get(aPrincipal), false, aGlobal) ||
1881 !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
1882 return NS_ERROR_FAILURE;
1885 MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
1887 // Set the location information for the new global, so that tools like
1888 // about:memory may use that information
1889 xpc::SetLocationForGlobal(aGlobal, aURI);
1891 if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) {
1892 return NS_ERROR_FAILURE;
1895 return NS_OK;
1898 nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
1899 nsISupports* aState,
1900 bool aForceReuseInnerWindow) {
1901 MOZ_ASSERT(mDocumentPrincipal == nullptr,
1902 "mDocumentPrincipal prematurely set!");
1903 MOZ_ASSERT(mDocumentStoragePrincipal == nullptr,
1904 "mDocumentStoragePrincipal prematurely set!");
1905 MOZ_ASSERT(aDocument);
1907 // Bail out early if we're in process of closing down the window.
1908 NS_ENSURE_STATE(!mCleanedUp);
1910 NS_ASSERTION(!GetCurrentInnerWindow() ||
1911 GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
1912 "Uh, mDoc doesn't match the current inner window "
1913 "document!");
1915 bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
1916 if (aForceReuseInnerWindow && !wouldReuseInnerWindow && mDoc &&
1917 mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
1918 NS_ERROR("Attempted forced inner window reuse while changing principal");
1919 return NS_ERROR_UNEXPECTED;
1922 RefPtr<Document> oldDoc = mDoc;
1923 MOZ_RELEASE_ASSERT(oldDoc != aDocument);
1925 AutoJSAPI jsapi;
1926 jsapi.Init();
1927 JSContext* cx = jsapi.cx();
1929 // Check if we're anywhere near the stack limit before we reach the
1930 // transplanting code, since it has no good way to handle errors. This uses
1931 // the untrusted script limit, which is not strictly necessary since no
1932 // actual script should run.
1933 if (!js::CheckRecursionLimitConservativeDontReport(cx)) {
1934 NS_WARNING("Overrecursion in SetNewDocument");
1935 return NS_ERROR_FAILURE;
1938 if (!mDoc) {
1939 // First document load.
1941 // Get our private root. If it is equal to us, then we need to
1942 // attach our global key bindings that handles browser scrolling
1943 // and other browser commands.
1944 nsPIDOMWindowOuter* privateRoot = GetPrivateRoot();
1946 if (privateRoot == this) {
1947 nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler);
1951 /* No mDocShell means we're already been partially closed down. When that
1952 happens, setting status isn't a big requirement, so don't. (Doesn't happen
1953 under normal circumstances, but bug 49615 describes a case.) */
1955 nsContentUtils::AddScriptRunner(
1956 NewRunnableMethod("nsGlobalWindowOuter::ClearStatus", this,
1957 &nsGlobalWindowOuter::ClearStatus));
1959 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
1960 // window (see bug 776497). Be safe.
1961 bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
1962 GetCurrentInnerWindowInternal();
1964 nsresult rv;
1966 // We set mDoc even though this is an outer window to avoid
1967 // having to *always* reach into the inner window to find the
1968 // document.
1969 mDoc = aDocument;
1971 // Take this opportunity to clear mSuspendedDoc. Our old inner window is now
1972 // responsible for unsuspending it.
1973 mSuspendedDoc = nullptr;
1975 #ifdef DEBUG
1976 mLastOpenedURI = aDocument->GetDocumentURI();
1977 #endif
1979 mContext->WillInitializeContext();
1981 RefPtr<nsGlobalWindowInner> currentInner = GetCurrentInnerWindowInternal();
1983 if (currentInner && currentInner->mNavigator) {
1984 currentInner->mNavigator->OnNavigation();
1987 RefPtr<nsGlobalWindowInner> newInnerWindow;
1988 bool createdInnerWindow = false;
1990 bool thisChrome = IsChromeWindow();
1992 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
1993 NS_ASSERTION(!aState || wsh,
1994 "What kind of weird state are you giving me here?");
1996 bool doomCurrentInner = false;
1998 // Only non-gray (i.e. exposed to JS) objects should be assigned to
1999 // newInnerGlobal.
2000 JS::Rooted<JSObject*> newInnerGlobal(cx);
2001 if (reUseInnerWindow) {
2002 // We're reusing the current inner window.
2003 NS_ASSERTION(!currentInner->IsFrozen(),
2004 "We should never be reusing a shared inner window");
2005 newInnerWindow = currentInner;
2006 newInnerGlobal = currentInner->GetWrapper();
2008 // We're reusing the inner window, but this still counts as a navigation,
2009 // so all expandos and such defined on the outer window should go away.
2010 // Force all Xray wrappers to be recomputed.
2011 JS::Rooted<JSObject*> rootedObject(cx, GetWrapper());
2012 if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
2013 return NS_ERROR_FAILURE;
2016 // Inner windows are only reused for same-origin principals, but the
2017 // principals don't necessarily match exactly. Update the principal on the
2018 // realm to match the new document. NB: We don't just call
2019 // currentInner->RefreshRealmPrincipals() here because we haven't yet set
2020 // its mDoc to aDocument.
2021 JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal);
2022 #ifdef DEBUG
2023 bool sameOrigin = false;
2024 nsIPrincipal* existing = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
2025 aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
2026 MOZ_ASSERT(sameOrigin);
2027 #endif
2028 JS::SetRealmPrincipals(realm,
2029 nsJSPrincipals::get(aDocument->NodePrincipal()));
2030 } else {
2031 if (aState) {
2032 newInnerWindow = wsh->GetInnerWindow();
2033 newInnerGlobal = newInnerWindow->GetWrapper();
2034 } else {
2035 newInnerWindow = nsGlobalWindowInner::Create(this, thisChrome);
2036 if (StaticPrefs::dom_timeout_defer_during_load()) {
2037 // ensure the initial loading state is known
2038 newInnerWindow->SetActiveLoadingState(
2039 aDocument->GetReadyStateEnum() ==
2040 Document::ReadyState::READYSTATE_LOADING);
2043 // The outer window is automatically treated as frozen when we
2044 // null out the inner window. As a result, initializing classes
2045 // on the new inner won't end up reaching into the old inner
2046 // window for classes etc.
2048 // [This happens with Object.prototype when XPConnect creates
2049 // a temporary global while initializing classes; the reason
2050 // being that xpconnect creates the temp global w/o a parent
2051 // and proto, which makes the JS engine look up classes in
2052 // cx->globalObject, i.e. this outer window].
2054 mInnerWindow = nullptr;
2056 mCreatingInnerWindow = true;
2057 // Every script context we are initialized with must create a
2058 // new global.
2059 rv = CreateNativeGlobalForInner(
2060 cx, newInnerWindow, aDocument->GetDocumentURI(),
2061 aDocument->NodePrincipal(), &newInnerGlobal,
2062 ComputeIsSecureContext(aDocument));
2063 NS_ASSERTION(
2064 NS_SUCCEEDED(rv) && newInnerGlobal &&
2065 newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
2066 "Failed to get script global");
2068 mCreatingInnerWindow = false;
2069 createdInnerWindow = true;
2071 NS_ENSURE_SUCCESS(rv, rv);
2074 if (currentInner && currentInner->GetWrapperPreserveColor()) {
2075 // Don't free objects on our current inner window if it's going to be
2076 // held in the bfcache.
2077 if (!currentInner->IsFrozen()) {
2078 doomCurrentInner = true;
2082 mInnerWindow = newInnerWindow;
2083 MOZ_ASSERT(mInnerWindow);
2084 mInnerWindow->TryToCacheTopInnerWindow();
2086 if (!GetWrapperPreserveColor()) {
2087 JS::Rooted<JSObject*> outer(
2088 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2089 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
2091 js::SetProxyReservedSlot(outer, OUTER_WINDOW_SLOT,
2092 js::PrivateValue(ToSupports(this)));
2094 // Inform the nsJSContext, which is the canonical holder of the outer.
2095 mContext->SetWindowProxy(outer);
2096 mContext->DidInitializeContext();
2098 SetWrapper(mContext->GetWindowProxy());
2099 } else {
2100 JS::Rooted<JSObject*> outerObject(
2101 cx, NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2102 if (!outerObject) {
2103 NS_ERROR("out of memory");
2104 return NS_ERROR_FAILURE;
2107 JS::Rooted<JSObject*> obj(cx, GetWrapper());
2109 MOZ_ASSERT(js::IsWindowProxy(obj));
2111 js::SetProxyReservedSlot(obj, OUTER_WINDOW_SLOT,
2112 js::PrivateValue(nullptr));
2113 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
2114 js::PrivateValue(nullptr));
2115 js::SetProxyReservedSlot(obj, HOLDER_WEAKMAP_SLOT, JS::UndefinedValue());
2117 outerObject = xpc::TransplantObject(cx, obj, outerObject);
2118 if (!outerObject) {
2119 mBrowsingContext->ClearWindowProxy();
2120 NS_ERROR("unable to transplant wrappers, probably OOM");
2121 return NS_ERROR_FAILURE;
2124 js::SetProxyReservedSlot(outerObject, OUTER_WINDOW_SLOT,
2125 js::PrivateValue(ToSupports(this)));
2127 SetWrapper(outerObject);
2129 MOZ_ASSERT(JS::GetNonCCWObjectGlobal(outerObject) == newInnerGlobal);
2131 // Inform the nsJSContext, which is the canonical holder of the outer.
2132 mContext->SetWindowProxy(outerObject);
2135 // Enter the new global's realm.
2136 JSAutoRealm ar(cx, GetWrapperPreserveColor());
2139 JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor());
2140 js::SetWindowProxy(cx, newInnerGlobal, outer);
2141 mBrowsingContext->SetWindowProxy(outer);
2144 // Set scriptability based on the state of the docshell.
2145 bool allow = GetDocShell()->GetCanExecuteScripts();
2146 xpc::Scriptability::Get(GetWrapperPreserveColor())
2147 .SetDocShellAllowsScript(allow);
2149 if (!aState) {
2150 // Get the "window" property once so it will be cached on our inner. We
2151 // have to do this here, not in binding code, because this has to happen
2152 // after we've created the outer window proxy and stashed it in the outer
2153 // nsGlobalWindowOuter, so GetWrapperPreserveColor() on that outer
2154 // nsGlobalWindowOuter doesn't return null and
2155 // nsGlobalWindowOuter::OuterObject works correctly.
2156 JS::Rooted<JS::Value> unused(cx);
2157 if (!JS_GetProperty(cx, newInnerGlobal, "window", &unused)) {
2158 NS_ERROR("can't create the 'window' property");
2159 return NS_ERROR_FAILURE;
2162 // And same thing for the "self" property.
2163 if (!JS_GetProperty(cx, newInnerGlobal, "self", &unused)) {
2164 NS_ERROR("can't create the 'self' property");
2165 return NS_ERROR_FAILURE;
2170 JSAutoRealm ar(cx, GetWrapperPreserveColor());
2172 if (!aState && !reUseInnerWindow) {
2173 // Loading a new page and creating a new inner window, *not*
2174 // restoring from session history.
2176 // Now that both the the inner and outer windows are initialized
2177 // let the script context do its magic to hook them together.
2178 MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
2179 #ifdef DEBUG
2180 JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
2181 JS::Rooted<JSObject*> proto1(cx), proto2(cx);
2182 JS_GetPrototype(cx, rootedJSObject, &proto1);
2183 JS_GetPrototype(cx, newInnerGlobal, &proto2);
2184 NS_ASSERTION(proto1 == proto2,
2185 "outer and inner globals should have the same prototype");
2186 #endif
2188 mInnerWindow->SyncStateFromParentWindow();
2191 // Add an extra ref in case we release mContext during GC.
2192 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
2194 aDocument->SetScriptGlobalObject(newInnerWindow);
2195 MOZ_ASSERT(newInnerWindow->mTabGroup,
2196 "We must have a TabGroup cached at this point");
2198 if (!aState) {
2199 if (reUseInnerWindow) {
2200 MOZ_RELEASE_ASSERT(newInnerWindow->mDoc != aDocument);
2201 newInnerWindow->mDoc = aDocument;
2203 // The storage objects contain the URL of the window. We have to
2204 // recreate them when the innerWindow is reused.
2205 newInnerWindow->mLocalStorage = nullptr;
2206 newInnerWindow->mSessionStorage = nullptr;
2207 newInnerWindow->mPerformance = nullptr;
2209 // This must be called after nullifying the internal objects because
2210 // here we could recreate them, calling the getter methods, and store
2211 // them into the JS slots. If we nullify them after, the slot values and
2212 // the objects will be out of sync.
2213 newInnerWindow->ClearDocumentDependentSlots(cx);
2215 // When replacing an initial about:blank document we call
2216 // ExecutionReady again to update the client creation URL.
2217 rv = newInnerWindow->ExecutionReady();
2218 NS_ENSURE_SUCCESS(rv, rv);
2219 } else {
2220 newInnerWindow->InnerSetNewDocument(cx, aDocument);
2222 // Initialize DOM classes etc on the inner window.
2223 JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
2224 rv = kungFuDeathGrip->InitClasses(obj);
2225 NS_ENSURE_SUCCESS(rv, rv);
2227 rv = newInnerWindow->ExecutionReady();
2228 NS_ENSURE_SUCCESS(rv, rv);
2231 if (mArguments) {
2232 newInnerWindow->DefineArgumentsProperty(mArguments);
2233 mArguments = nullptr;
2236 // Give the new inner window our chrome event handler (since it
2237 // doesn't have one).
2238 newInnerWindow->mChromeEventHandler = mChromeEventHandler;
2241 // Tell the WindowGlobalParent that it should become the current window global
2242 // for our BrowsingContext if it isn't already.
2243 mInnerWindow->GetWindowGlobalChild()->SendUpdateDocumentURI(
2244 aDocument->GetDocumentURI());
2245 mInnerWindow->GetWindowGlobalChild()->SendBecomeCurrentWindowGlobal();
2247 // We no longer need the old inner window. Start its destruction if
2248 // its not being reused and clear our reference.
2249 if (doomCurrentInner) {
2250 currentInner->FreeInnerObjects();
2252 currentInner = nullptr;
2254 // Ask the JS engine to assert that it's valid to access our DocGroup whenever
2255 // it runs JS code for this realm. We skip the check if this window is for
2256 // chrome JS or an add-on.
2257 nsCOMPtr<nsIPrincipal> principal = mDoc->NodePrincipal();
2258 if (GetDocGroup() && !nsContentUtils::IsSystemPrincipal(principal) &&
2259 !BasePrincipal::Cast(principal)->AddonPolicy()) {
2260 js::SetRealmValidAccessPtr(
2261 cx, newInnerGlobal, newInnerWindow->GetDocGroup()->GetValidAccessPtr());
2264 kungFuDeathGrip->DidInitializeContext();
2266 // We wait to fire the debugger hook until the window is all set up and hooked
2267 // up with the outer. See bug 969156.
2268 if (createdInnerWindow) {
2269 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2270 "nsGlobalWindowInner::FireOnNewGlobalObject", newInnerWindow,
2271 &nsGlobalWindowInner::FireOnNewGlobalObject));
2274 if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
2275 // We should probably notify. However if this is the, arguably bad,
2276 // situation when we're creating a temporary non-chrome-about-blank
2277 // document in a chrome docshell, don't notify just yet. Instead wait
2278 // until we have a real chrome doc.
2279 if (!mDocShell ||
2280 mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome ||
2281 nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) {
2282 newInnerWindow->mHasNotifiedGlobalCreated = true;
2283 nsContentUtils::AddScriptRunner(NewRunnableMethod(
2284 "nsGlobalWindowOuter::DispatchDOMWindowCreated", this,
2285 &nsGlobalWindowOuter::DispatchDOMWindowCreated));
2289 PreloadLocalStorage();
2291 // If we have a recorded interesting Large-Allocation header status, report it
2292 // to the newly attached document.
2293 ReportLargeAllocStatus();
2294 mLargeAllocStatus = LargeAllocStatus::NONE;
2296 mHasStorageAccess = false;
2297 nsIURI* uri = aDocument->GetDocumentURI();
2298 if (newInnerWindow &&
2299 aDocument->CookieSettings()->GetRejectThirdPartyTrackers() &&
2300 nsContentUtils::IsThirdPartyWindowOrChannel(newInnerWindow, nullptr,
2301 uri) &&
2302 nsContentUtils::IsTrackingResourceWindow(newInnerWindow)) {
2303 // Grant storage access by default if the first-party storage access
2304 // permission has been granted already.
2305 // Don't notify in this case, since we would be notifying the user
2306 // needlessly.
2307 mHasStorageAccess = AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
2308 newInnerWindow, uri, nullptr);
2311 return NS_OK;
2314 void nsGlobalWindowOuter::PreloadLocalStorage() {
2315 if (!Storage::StoragePrefIsEnabled()) {
2316 return;
2319 if (IsChromeWindow()) {
2320 return;
2323 nsIPrincipal* principal = GetPrincipal();
2324 if (!principal) {
2325 return;
2328 nsresult rv;
2330 nsCOMPtr<nsIDOMStorageManager> storageManager =
2331 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
2332 if (NS_FAILED(rv)) {
2333 return;
2336 // private browsing windows do not persist local storage to disk so we should
2337 // only try to precache storage when we're not a private browsing window.
2338 if (principal->GetPrivateBrowsingId() == 0) {
2339 RefPtr<Storage> storage;
2340 rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
2341 if (NS_SUCCEEDED(rv)) {
2342 mLocalStorage = storage;
2347 void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
2348 if (!mDoc) {
2349 return;
2352 // Fire DOMWindowCreated at chrome event listeners
2353 nsContentUtils::DispatchChromeEvent(mDoc, ToSupports(mDoc),
2354 NS_LITERAL_STRING("DOMWindowCreated"),
2355 CanBubble::eYes, Cancelable::eNo);
2357 nsCOMPtr<nsIObserverService> observerService =
2358 mozilla::services::GetObserverService();
2360 // The event dispatching could possibly cause docshell destory, and
2361 // consequently cause mDoc to be set to nullptr by DropOuterWindowDocs(),
2362 // so check it again here.
2363 if (observerService && mDoc) {
2364 nsAutoString origin;
2365 nsIPrincipal* principal = mDoc->NodePrincipal();
2366 nsContentUtils::GetUTFOrigin(principal, origin);
2367 observerService->NotifyObservers(
2368 static_cast<nsIDOMWindow*>(this),
2369 nsContentUtils::IsSystemPrincipal(principal)
2370 ? "chrome-document-global-created"
2371 : "content-document-global-created",
2372 origin.get());
2376 void nsGlobalWindowOuter::ClearStatus() { SetStatusOuter(EmptyString()); }
2378 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
2379 MOZ_ASSERT(aDocShell);
2381 if (aDocShell == mDocShell) {
2382 return;
2385 mDocShell = aDocShell;
2386 mBrowsingContext = aDocShell->GetBrowsingContext();
2388 nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetScriptableParentOrNull();
2389 MOZ_RELEASE_ASSERT(!parentWindow || !mTabGroup ||
2390 mTabGroup ==
2391 nsGlobalWindowOuter::Cast(parentWindow)->mTabGroup);
2393 mTopLevelOuterContentWindow =
2394 !mIsChrome && GetScriptableTopInternal() == this;
2396 if (mFrames) {
2397 mFrames->SetDocShell(aDocShell);
2400 // Get our enclosing chrome shell and retrieve its global window impl, so
2401 // that we can do some forwarding to the chrome document.
2402 RefPtr<EventTarget> chromeEventHandler;
2403 mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2404 mChromeEventHandler = chromeEventHandler;
2405 if (!mChromeEventHandler) {
2406 // We have no chrome event handler. If we have a parent,
2407 // get our chrome event handler from the parent. If
2408 // we don't have a parent, then we need to make a new
2409 // window root object that will function as a chrome event
2410 // handler and receive all events that occur anywhere inside
2411 // our window.
2412 nsCOMPtr<nsPIDOMWindowOuter> parentWindow = GetParent();
2413 if (parentWindow.get() != this) {
2414 mChromeEventHandler = parentWindow->GetChromeEventHandler();
2415 } else {
2416 mChromeEventHandler = NS_NewWindowRoot(this);
2417 mIsRootOuterWindow = true;
2421 bool docShellActive;
2422 mDocShell->GetIsActive(&docShellActive);
2423 SetIsBackgroundInternal(!docShellActive);
2426 void nsGlobalWindowOuter::DetachFromDocShell() {
2427 // DetachFromDocShell means the window is being torn down. Drop our
2428 // reference to the script context, allowing it to be deleted
2429 // later. Meanwhile, keep our weak reference to the script object
2430 // so that it can be retrieved later (until it is finalized by the JS GC).
2432 // Call FreeInnerObjects on all inner windows, not just the current
2433 // one, since some could be held by WindowStateHolder objects that
2434 // are GC-owned.
2435 RefPtr<nsGlobalWindowInner> inner;
2436 for (PRCList* node = PR_LIST_HEAD(this); node != this;
2437 node = PR_NEXT_LINK(inner)) {
2438 // This cast is safe because `node != this`. Non-this nodes are inner
2439 // windows.
2440 inner = static_cast<nsGlobalWindowInner*>(node);
2441 MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == this);
2442 inner->FreeInnerObjects();
2445 // Don't report that we were detached to the nsWindowMemoryReporter, as it
2446 // only tracks inner windows.
2448 NotifyWindowIDDestroyed("outer-window-destroyed");
2450 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal();
2452 if (currentInner) {
2453 NS_ASSERTION(mDoc, "Must have doc!");
2455 // Remember the document's principal and URI.
2456 mDocumentPrincipal = mDoc->NodePrincipal();
2457 mDocumentStoragePrincipal = mDoc->EffectiveStoragePrincipal();
2458 mDocumentURI = mDoc->GetDocumentURI();
2460 // Release our document reference
2461 DropOuterWindowDocs();
2464 ClearControllers();
2466 mChromeEventHandler = nullptr; // force release now
2468 if (mContext) {
2469 // When we're about to destroy a top level content window
2470 // (for example a tab), we trigger a full GC by passing null as the last
2471 // param. We also trigger a full GC for chrome windows.
2472 nsJSContext::PokeGC(JS::GCReason::SET_DOC_SHELL,
2473 (mTopLevelOuterContentWindow || mIsChrome)
2474 ? nullptr
2475 : GetWrapperPreserveColor());
2476 mContext = nullptr;
2479 mDocShell = nullptr;
2480 mBrowsingContext->ClearDocShell();
2482 if (mFrames) {
2483 mFrames->SetDocShell(nullptr);
2486 MaybeForgiveSpamCount();
2487 CleanUp();
2490 void nsGlobalWindowOuter::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
2491 bool aOriginalOpener) {
2492 nsWeakPtr opener = do_GetWeakReference(aOpener);
2493 if (opener == mOpener) {
2494 MOZ_DIAGNOSTIC_ASSERT(!aOpener || !aOpener->GetDocShell() ||
2495 (GetBrowsingContext() &&
2496 aOpener->GetBrowsingContext() &&
2497 aOpener->GetBrowsingContext()->Id() ==
2498 GetBrowsingContext()->GetOpenerId()));
2499 return;
2502 NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled,
2503 "aOriginalOpener is true, but not first call to "
2504 "SetOpenerWindow!");
2505 NS_ASSERTION(aOpener || !aOriginalOpener,
2506 "Shouldn't set mHadOriginalOpener if aOpener is null");
2508 mOpener = opener.forget();
2509 NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
2511 if (mDocShell) {
2512 MOZ_DIAGNOSTIC_ASSERT(
2513 !aOriginalOpener || !aOpener ||
2514 // TODO(farre): Allowing to set a closed or closing window as
2515 // opener is not ideal, since it won't have a docshell and
2516 // therefore no browsing context. This means that we're
2517 // effectively setting the browsing context opener to null and
2518 // the window opener to a closed window. This needs to be
2519 // cleaned up, see Bug 1511353.
2520 nsGlobalWindowOuter::Cast(aOpener)->IsClosedOrClosing() ||
2521 // TODO(farre): Allowing to set an opener on a closed window is
2522 // not ideal either, but we need to allow it for now. Bug 1543056.
2523 IsClosedOrClosing() ||
2524 aOpener->GetBrowsingContext()->Id() ==
2525 GetBrowsingContext()->GetOpenerId());
2526 // TODO(farre): Here we really wish to only consider the case
2527 // where 'aOriginalOpener'. See bug 1509016.
2528 GetBrowsingContext()->SetOpener(aOpener ? aOpener->GetBrowsingContext()
2529 : nullptr);
2532 // Check that the js visible opener matches! We currently don't depend on this
2533 // being true outside of nightly, so we disable the assertion in optimized
2534 // release / beta builds.
2535 nsPIDOMWindowOuter* contentOpener = GetSanitizedOpener(aOpener);
2537 // contentOpener is not used when the DIAGNOSTIC_ASSERT is compiled out.
2538 mozilla::Unused << contentOpener;
2539 MOZ_DIAGNOSTIC_ASSERT(
2540 !contentOpener || !mTabGroup ||
2541 mTabGroup == nsGlobalWindowOuter::Cast(contentOpener)->mTabGroup);
2543 if (aOriginalOpener) {
2544 MOZ_ASSERT(!mHadOriginalOpener,
2545 "Probably too late to call ComputeIsSecureContext again");
2546 mHadOriginalOpener = true;
2549 #ifdef DEBUG
2550 mSetOpenerWindowCalled = true;
2551 #endif
2554 void nsGlobalWindowOuter::UpdateParentTarget() {
2555 // NOTE: This method is nearly identical to
2556 // nsGlobalWindowInner::UpdateParentTarget(). IF YOU UPDATE THIS METHOD,
2557 // UPDATE THE OTHER ONE TOO! The one difference is that this method updates
2558 // mMessageManager as well, which inner windows don't have.
2560 // Try to get our frame element's tab child global (its in-process message
2561 // manager). If that fails, fall back to the chrome event handler's tab
2562 // child global, and if it doesn't have one, just use the chrome event
2563 // handler itself.
2565 nsCOMPtr<Element> frameElement = GetFrameElementInternal();
2566 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2568 if (!mMessageManager) {
2569 nsGlobalWindowOuter* topWin = GetScriptableTopInternal();
2570 if (topWin) {
2571 frameElement = topWin->GetFrameElementInternal();
2572 mMessageManager = nsContentUtils::TryGetBrowserChildGlobal(frameElement);
2576 if (!mMessageManager) {
2577 mMessageManager =
2578 nsContentUtils::TryGetBrowserChildGlobal(mChromeEventHandler);
2581 if (mMessageManager) {
2582 mParentTarget = mMessageManager;
2583 } else {
2584 mParentTarget = mChromeEventHandler;
2588 EventTarget* nsGlobalWindowOuter::GetTargetForEventTargetChain() {
2589 return GetCurrentInnerWindowInternal();
2592 void nsGlobalWindowOuter::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
2593 MOZ_CRASH("The outer window should not be part of an event path");
2596 bool nsGlobalWindowOuter::ShouldPromptToBlockDialogs() {
2597 nsGlobalWindowOuter* topWindowOuter = GetScriptableTopInternal();
2598 if (!topWindowOuter) {
2599 NS_ASSERTION(!mDocShell,
2600 "ShouldPromptToBlockDialogs() called without a top window?");
2601 return true;
2604 nsGlobalWindowInner* topWindow =
2605 topWindowOuter->GetCurrentInnerWindowInternal();
2606 if (!topWindow) {
2607 return true;
2610 return topWindow->DialogsAreBeingAbused();
2613 bool nsGlobalWindowOuter::AreDialogsEnabled() {
2614 nsGlobalWindowOuter* topWindowOuter = GetScriptableTopInternal();
2615 if (!topWindowOuter) {
2616 NS_ERROR("AreDialogsEnabled() called without a top window?");
2617 return false;
2620 // TODO: Warn if no top window?
2621 nsGlobalWindowInner* topWindow =
2622 topWindowOuter->GetCurrentInnerWindowInternal();
2623 if (!topWindow) {
2624 return false;
2627 // Dialogs are blocked if the content viewer is hidden
2628 if (mDocShell) {
2629 nsCOMPtr<nsIContentViewer> cv;
2630 mDocShell->GetContentViewer(getter_AddRefs(cv));
2632 bool isHidden;
2633 cv->GetIsHidden(&isHidden);
2634 if (isHidden) {
2635 return false;
2639 // Dialogs are also blocked if the document is sandboxed with SANDBOXED_MODALS
2640 // (or if we have no document, of course). Which document? Who knows; the
2641 // spec is daft. See <https://github.com/whatwg/html/issues/1206>. For now
2642 // just go ahead and check mDoc, since in everything except edge cases in
2643 // which a frame is allow-same-origin but not allow-scripts and is being poked
2644 // at by some other window this should be the right thing anyway.
2645 if (!mDoc || (mDoc->GetSandboxFlags() & SANDBOXED_MODALS)) {
2646 return false;
2649 return topWindow->mAreDialogsEnabled;
2652 bool nsGlobalWindowOuter::ConfirmDialogIfNeeded() {
2653 NS_ENSURE_TRUE(mDocShell, false);
2654 nsCOMPtr<nsIPromptService> promptSvc =
2655 do_GetService("@mozilla.org/embedcomp/prompt-service;1");
2657 if (!promptSvc) {
2658 return true;
2661 // Reset popup state while opening a modal dialog, and firing events
2662 // about the dialog, to prevent the current state from being active
2663 // the whole time a modal dialog is open.
2664 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
2666 bool disableDialog = false;
2667 nsAutoString label, title;
2668 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
2669 "ScriptDialogLabel", label);
2670 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
2671 "ScriptDialogPreventTitle", title);
2672 promptSvc->Confirm(this, title.get(), label.get(), &disableDialog);
2673 if (disableDialog) {
2674 DisableDialogs();
2675 return false;
2678 return true;
2681 void nsGlobalWindowOuter::DisableDialogs() {
2682 nsGlobalWindowOuter* topWindowOuter = GetScriptableTopInternal();
2683 if (!topWindowOuter) {
2684 NS_ERROR("DisableDialogs() called without a top window?");
2685 return;
2688 nsGlobalWindowInner* topWindow =
2689 topWindowOuter->GetCurrentInnerWindowInternal();
2690 // TODO: Warn if no top window?
2691 if (topWindow) {
2692 topWindow->mAreDialogsEnabled = false;
2696 void nsGlobalWindowOuter::EnableDialogs() {
2697 nsGlobalWindowOuter* topWindowOuter = GetScriptableTopInternal();
2698 if (!topWindowOuter) {
2699 NS_ERROR("EnableDialogs() called without a top window?");
2700 return;
2703 // TODO: Warn if no top window?
2704 nsGlobalWindowInner* topWindow =
2705 topWindowOuter->GetCurrentInnerWindowInternal();
2706 if (topWindow) {
2707 topWindow->mAreDialogsEnabled = true;
2711 nsresult nsGlobalWindowOuter::PostHandleEvent(EventChainPostVisitor& aVisitor) {
2712 MOZ_CRASH("The outer window should not be part of an event path");
2715 void nsGlobalWindowOuter::PoisonOuterWindowProxy(JSObject* aObject) {
2716 if (aObject == GetWrapperMaybeDead()) {
2717 PoisonWrapper();
2721 nsresult nsGlobalWindowOuter::SetArguments(nsIArray* aArguments) {
2722 nsresult rv;
2724 // Historically, we've used the same machinery to handle openDialog arguments
2725 // (exposed via window.arguments) and showModalDialog arguments (exposed via
2726 // window.dialogArguments), even though the former is XUL-only and uses an
2727 // XPCOM array while the latter is web-exposed and uses an arbitrary JS value.
2728 // Moreover, per-spec |dialogArguments| is a property of the browsing context
2729 // (outer), whereas |arguments| lives on the inner.
2731 // We've now mostly separated them, but the difference is still opaque to
2732 // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
2733 // embedding waltz we do here).
2735 // So we need to demultiplex the two cases here.
2736 nsGlobalWindowInner* currentInner = GetCurrentInnerWindowInternal();
2738 mArguments = aArguments;
2739 rv = currentInner->DefineArgumentsProperty(aArguments);
2740 NS_ENSURE_SUCCESS(rv, rv);
2742 return NS_OK;
2745 //*****************************************************************************
2746 // nsGlobalWindowOuter::nsIScriptObjectPrincipal
2747 //*****************************************************************************
2749 nsIPrincipal* nsGlobalWindowOuter::GetPrincipal() {
2750 if (mDoc) {
2751 // If we have a document, get the principal from the document
2752 return mDoc->NodePrincipal();
2755 if (mDocumentPrincipal) {
2756 return mDocumentPrincipal;
2759 // If we don't have a principal and we don't have a document we
2760 // ask the parent window for the principal. This can happen when
2761 // loading a frameset that has a <frame src="javascript:xxx">, in
2762 // that case the global window is used in JS before we've loaded
2763 // a document into the window.
2765 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2766 do_QueryInterface(GetParentInternal());
2768 if (objPrincipal) {
2769 return objPrincipal->GetPrincipal();
2772 return nullptr;
2775 nsIPrincipal* nsGlobalWindowOuter::GetEffectiveStoragePrincipal() {
2776 if (mDoc) {
2777 // If we have a document, get the principal from the document
2778 return mDoc->EffectiveStoragePrincipal();
2781 if (mDocumentStoragePrincipal) {
2782 return mDocumentStoragePrincipal;
2785 // If we don't have a storage principal and we don't have a document we ask
2786 // the parent window for the storage principal.
2788 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
2789 do_QueryInterface(GetParentInternal());
2791 if (objPrincipal) {
2792 return objPrincipal->GetEffectiveStoragePrincipal();
2795 return nullptr;
2798 //*****************************************************************************
2799 // nsGlobalWindowOuter::nsIDOMWindow
2800 //*****************************************************************************
2802 void nsPIDOMWindowOuter::SetInitialKeyboardIndicators(
2803 UIStateChangeType aShowFocusRings) {
2804 MOZ_ASSERT(!GetCurrentInnerWindow());
2806 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
2807 if (!piWin) {
2808 return;
2811 MOZ_ASSERT(piWin == this);
2813 // only change the flags that have been modified
2814 nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler);
2815 if (!windowRoot) {
2816 return;
2819 if (aShowFocusRings != UIStateChangeType_NoChange) {
2820 windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set);
2823 nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(this, aShowFocusRings);
2826 Element* nsPIDOMWindowOuter::GetFrameElementInternal() const {
2827 return mFrameElement;
2830 void nsPIDOMWindowOuter::SetFrameElementInternal(Element* aFrameElement) {
2831 mFrameElement = aFrameElement;
2834 Navigator* nsGlobalWindowOuter::GetNavigator() {
2835 FORWARD_TO_INNER(Navigator, (), nullptr);
2838 nsScreen* nsGlobalWindowOuter::GetScreen() {
2839 FORWARD_TO_INNER(GetScreen, (IgnoreErrors()), nullptr);
2842 void nsPIDOMWindowOuter::MaybeActiveMediaComponents() {
2843 if (mMediaSuspend != nsISuspendedTypes::SUSPENDED_BLOCK) {
2844 return;
2847 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
2848 ("nsPIDOMWindowOuter, MaybeActiveMediaComponents, "
2849 "resume the window from blocked, this = %p\n",
2850 this));
2852 SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
2855 SuspendTypes nsPIDOMWindowOuter::GetMediaSuspend() const {
2856 return mMediaSuspend;
2859 void nsPIDOMWindowOuter::SetMediaSuspend(SuspendTypes aSuspend) {
2860 if (!IsDisposableSuspend(aSuspend)) {
2861 MaybeNotifyMediaResumedFromBlock(aSuspend);
2862 mMediaSuspend = aSuspend;
2865 RefreshMediaElementsSuspend(aSuspend);
2868 void nsPIDOMWindowOuter::MaybeNotifyMediaResumedFromBlock(
2869 SuspendTypes aSuspend) {
2870 if (mMediaSuspend == nsISuspendedTypes::SUSPENDED_BLOCK &&
2871 aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
2872 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2873 if (service) {
2874 service->NotifyMediaResumedFromBlock(this);
2879 bool nsPIDOMWindowOuter::GetAudioMuted() const { return mAudioMuted; }
2881 void nsPIDOMWindowOuter::SetAudioMuted(bool aMuted) {
2882 if (mAudioMuted == aMuted) {
2883 return;
2886 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
2887 ("nsPIDOMWindowOuter %p, SetAudioMuted=%s", this,
2888 aMuted ? "muted" : "unmuted"));
2889 mAudioMuted = aMuted;
2890 RefreshMediaElementsVolume();
2893 float nsPIDOMWindowOuter::GetAudioVolume() const { return mAudioVolume; }
2895 nsresult nsPIDOMWindowOuter::SetAudioVolume(float aVolume) {
2896 if (aVolume < 0.0) {
2897 return NS_ERROR_DOM_INDEX_SIZE_ERR;
2900 if (mAudioVolume == aVolume) {
2901 return NS_OK;
2904 mAudioVolume = aVolume;
2905 RefreshMediaElementsVolume();
2906 return NS_OK;
2909 void nsPIDOMWindowOuter::RefreshMediaElementsVolume() {
2910 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2911 if (service) {
2912 service->RefreshAgentsVolume(this);
2916 void nsPIDOMWindowOuter::RefreshMediaElementsSuspend(SuspendTypes aSuspend) {
2917 RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
2918 if (service) {
2919 service->RefreshAgentsSuspend(this, aSuspend);
2923 bool nsPIDOMWindowOuter::IsDisposableSuspend(SuspendTypes aSuspend) const {
2924 return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
2925 aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
2928 void nsPIDOMWindowOuter::SetServiceWorkersTestingEnabled(bool aEnabled) {
2929 // Devtools should only be setting this on the top level window. Its
2930 // ok if devtools clears the flag on clean up of nested windows, though.
2931 // It will have no affect.
2932 #ifdef DEBUG
2933 nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetScriptableTop();
2934 MOZ_ASSERT_IF(aEnabled, this == topWindow);
2935 #endif
2936 mServiceWorkersTestingEnabled = aEnabled;
2939 bool nsPIDOMWindowOuter::GetServiceWorkersTestingEnabled() {
2940 // Automatically get this setting from the top level window so that nested
2941 // iframes get the correct devtools setting.
2942 nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetScriptableTop();
2943 if (!topWindow) {
2944 return false;
2946 return topWindow->mServiceWorkersTestingEnabled;
2949 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetParentOuter() {
2950 BrowsingContext* bc = GetBrowsingContext();
2951 return bc ? bc->GetParent(IgnoreErrors()) : nullptr;
2955 * GetScriptableParent is called when script reads window.parent.
2957 * In contrast to GetRealParent, GetScriptableParent respects <iframe
2958 * mozbrowser> boundaries, so if |this| is contained by an <iframe
2959 * mozbrowser>, we will return |this| as its own parent.
2961 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetScriptableParent() {
2962 if (!mDocShell) {
2963 return nullptr;
2966 if (mDocShell->GetIsMozBrowser()) {
2967 return this;
2970 nsCOMPtr<nsPIDOMWindowOuter> parent = GetParent();
2971 return parent;
2975 * Behavies identically to GetScriptableParent extept that it returns null
2976 * if GetScriptableParent would return this window.
2978 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetScriptableParentOrNull() {
2979 nsPIDOMWindowOuter* parent = GetScriptableParent();
2980 return (nsGlobalWindowOuter::Cast(parent) == this) ? nullptr : parent;
2984 * nsPIDOMWindow::GetParent (when called from C++) is just a wrapper around
2985 * GetRealParent.
2987 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetParent() {
2988 if (!mDocShell) {
2989 return nullptr;
2992 nsCOMPtr<nsIDocShell> parent;
2993 mDocShell->GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent));
2995 if (parent) {
2996 nsCOMPtr<nsPIDOMWindowOuter> win = parent->GetWindow();
2997 return win.forget();
3000 nsCOMPtr<nsPIDOMWindowOuter> win(this);
3001 return win.forget();
3004 static nsresult GetTopImpl(nsGlobalWindowOuter* aWin, nsIURI* aURIBeingLoaded,
3005 nsPIDOMWindowOuter** aTop, bool aScriptable,
3006 bool aExcludingExtensionAccessibleContentFrames) {
3007 *aTop = nullptr;
3009 MOZ_ASSERT_IF(aExcludingExtensionAccessibleContentFrames, !aScriptable);
3011 // Walk up the parent chain.
3013 nsCOMPtr<nsPIDOMWindowOuter> prevParent = aWin;
3014 nsCOMPtr<nsPIDOMWindowOuter> parent = aWin;
3015 do {
3016 if (!parent) {
3017 break;
3020 prevParent = parent;
3022 if (aScriptable) {
3023 parent = parent->GetScriptableParent();
3024 } else {
3025 parent = parent->GetParent();
3028 if (aExcludingExtensionAccessibleContentFrames) {
3029 if (auto* p = nsGlobalWindowOuter::Cast(parent)) {
3030 nsGlobalWindowInner* currentInner = p->GetCurrentInnerWindowInternal();
3031 nsIURI* uri = prevParent->GetDocumentURI();
3032 if (!uri) {
3033 // If our parent doesn't have a URI yet, we have a document that is in
3034 // the process of being loaded. In that case, our caller is
3035 // responsible for passing in the URI for the document that is being
3036 // loaded, so we fall back to using that URI here.
3037 uri = aURIBeingLoaded;
3040 if (currentInner && uri) {
3041 // If we find an inner window, we better find the uri for the current
3042 // window we're looking at. If we can't find it directly, it is the
3043 // responsibility of our caller to provide it to us.
3044 MOZ_DIAGNOSTIC_ASSERT(uri);
3046 // If the new parent has permission to load the current page, we're
3047 // at a moz-extension:// frame which has a host permission that allows
3048 // it to load the document that we've loaded. In that case, stop at
3049 // this frame and consider it the top-level frame.
3051 // Note that it's possible for the set of URIs accepted by
3052 // AddonAllowsLoad() to change at runtime, but we don't need to cache
3053 // the result of this check, since the important consumer of this code
3054 // (which is nsIHttpChannelInternal.topWindowURI) already caches the
3055 // result after computing it the first time.
3056 if (BasePrincipal::Cast(p->GetPrincipal())
3057 ->AddonAllowsLoad(uri, true)) {
3058 parent = prevParent;
3059 break;
3065 } while (parent != prevParent);
3067 if (parent) {
3068 parent.swap(*aTop);
3071 return NS_OK;
3075 * GetScriptableTop is called when script reads window.top.
3077 * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser>
3078 * boundaries. If we encounter a window owned by an <iframe mozbrowser> while
3079 * walking up the window hierarchy, we'll stop and return that window.
3081 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetScriptableTop() {
3082 nsCOMPtr<nsPIDOMWindowOuter> window;
3083 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
3084 /* aScriptable = */ true,
3085 /* aExcludingExtensionAccessibleContentFrames = */ false);
3086 return window.get();
3089 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetTop() {
3090 nsCOMPtr<nsPIDOMWindowOuter> window;
3091 GetTopImpl(this, /* aURIBeingLoaded = */ nullptr, getter_AddRefs(window),
3092 /* aScriptable = */ false,
3093 /* aExcludingExtensionAccessibleContentFrames = */ false);
3094 return window.forget();
3097 already_AddRefed<nsPIDOMWindowOuter>
3098 nsGlobalWindowOuter::GetTopExcludingExtensionAccessibleContentFrames(
3099 nsIURI* aURIBeingLoaded) {
3100 nsCOMPtr<nsPIDOMWindowOuter> window;
3101 GetTopImpl(this, aURIBeingLoaded, getter_AddRefs(window),
3102 /* aScriptable = */ false,
3103 /* aExcludingExtensionAccessibleContentFrames = */ true);
3104 return window.forget();
3107 void nsGlobalWindowOuter::GetContentOuter(JSContext* aCx,
3108 JS::MutableHandle<JSObject*> aRetval,
3109 CallerType aCallerType,
3110 ErrorResult& aError) {
3111 nsCOMPtr<nsPIDOMWindowOuter> content =
3112 GetContentInternal(aError, aCallerType);
3113 if (aError.Failed()) {
3114 return;
3117 if (content) {
3118 JS::Rooted<JS::Value> val(aCx);
3119 aError = nsContentUtils::WrapNative(aCx, content, &val);
3120 if (aError.Failed()) {
3121 return;
3124 aRetval.set(&val.toObject());
3125 return;
3128 aRetval.set(nullptr);
3131 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetContentInternal(
3132 ErrorResult& aError, CallerType aCallerType) {
3133 // First check for a named frame named "content"
3134 RefPtr<BrowsingContext> bc = GetChildWindow(NS_LITERAL_STRING("content"));
3135 if (bc) {
3136 nsCOMPtr<nsPIDOMWindowOuter> content(bc->GetDOMWindow());
3137 return content.forget();
3140 // If we're contained in <iframe mozbrowser>, then GetContent is the same as
3141 // window.top.
3142 if (mDocShell && mDocShell->GetIsInMozBrowser()) {
3143 nsCOMPtr<nsPIDOMWindowOuter> domWindow(GetScriptableTop());
3144 return domWindow.forget();
3147 nsCOMPtr<nsIDocShellTreeItem> primaryContent;
3148 if (aCallerType != CallerType::System) {
3149 if (mDoc) {
3150 mDoc->WarnOnceAbout(Document::eWindowContentUntrusted);
3152 // If we're called by non-chrome code, make sure we don't return
3153 // the primary content window if the calling tab is hidden. In
3154 // such a case we return the same-type root in the hidden tab,
3155 // which is "good enough", for now.
3156 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell));
3158 if (baseWin) {
3159 bool visible = false;
3160 baseWin->GetVisibility(&visible);
3162 if (!visible) {
3163 mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(primaryContent));
3168 if (!primaryContent) {
3169 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
3170 if (!treeOwner) {
3171 aError.Throw(NS_ERROR_FAILURE);
3172 return nullptr;
3175 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
3178 if (!primaryContent) {
3179 return nullptr;
3182 nsCOMPtr<nsPIDOMWindowOuter> domWindow = primaryContent->GetWindow();
3183 return domWindow.forget();
3186 nsresult nsGlobalWindowOuter::GetPrompter(nsIPrompt** aPrompt) {
3187 if (!mDocShell) return NS_ERROR_FAILURE;
3189 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
3190 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
3192 prompter.forget(aPrompt);
3193 return NS_OK;
3196 bool nsGlobalWindowOuter::GetClosedOuter() {
3197 // If someone called close(), or if we don't have a docshell, we're closed.
3198 return mIsClosed || !mDocShell;
3201 bool nsGlobalWindowOuter::Closed() { return GetClosedOuter(); }
3203 nsDOMWindowList* nsGlobalWindowOuter::GetFrames() {
3204 if (!mFrames && mDocShell) {
3205 mFrames = new nsDOMWindowList(mDocShell);
3208 return mFrames;
3211 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::IndexedGetterOuter(
3212 uint32_t aIndex) {
3213 nsDOMWindowList* windows = GetFrames();
3214 NS_ENSURE_TRUE(windows, nullptr);
3216 return windows->IndexedGetter(aIndex);
3219 nsIControllers* nsGlobalWindowOuter::GetControllersOuter(ErrorResult& aError) {
3220 if (!mControllers) {
3221 mControllers = new nsXULControllers();
3222 if (!mControllers) {
3223 aError.Throw(NS_ERROR_FAILURE);
3224 return nullptr;
3227 // Add in the default controller
3228 RefPtr<nsBaseCommandController> commandController =
3229 nsBaseCommandController::CreateWindowController();
3230 if (!commandController) {
3231 aError.Throw(NS_ERROR_FAILURE);
3232 return nullptr;
3235 mControllers->InsertControllerAt(0, commandController);
3236 commandController->SetCommandContext(static_cast<nsIDOMWindow*>(this));
3239 return mControllers;
3242 nsresult nsGlobalWindowOuter::GetControllers(nsIControllers** aResult) {
3243 FORWARD_TO_INNER(GetControllers, (aResult), NS_ERROR_UNEXPECTED);
3246 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetSanitizedOpener(
3247 nsPIDOMWindowOuter* aOpener) {
3248 if (!aOpener) {
3249 return nullptr;
3252 nsGlobalWindowOuter* win = nsGlobalWindowOuter::Cast(aOpener);
3254 // First, ensure that we're not handing back a chrome window to content:
3255 if (win->IsChromeWindow()) {
3256 return nullptr;
3259 // We don't want to reveal the opener if the opener is a mail window,
3260 // because opener can be used to spoof the contents of a message (bug 105050).
3261 // So, we look in the opener's root docshell to see if it's a mail window.
3262 nsCOMPtr<nsIDocShell> openerDocShell = aOpener->GetDocShell();
3264 if (openerDocShell) {
3265 nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
3266 openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem));
3267 nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
3268 if (openerRootDocShell) {
3269 nsIDocShell::AppType appType = openerRootDocShell->GetAppType();
3270 if (appType != nsIDocShell::APP_TYPE_MAIL) {
3271 return aOpener;
3276 return nullptr;
3279 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOpenerWindowOuter() {
3280 nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryReferent(mOpener);
3282 if (!opener) {
3283 return nullptr;
3286 // First, check if we were called from a privileged chrome script
3287 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
3288 // Catch the case where we're chrome but the opener is not...
3289 if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() &&
3290 nsGlobalWindowOuter::Cast(opener)->GetPrincipal() !=
3291 nsContentUtils::GetSystemPrincipal()) {
3292 return nullptr;
3294 return opener;
3297 return GetSanitizedOpener(opener);
3300 already_AddRefed<nsPIDOMWindowOuter> nsGlobalWindowOuter::GetOpener() {
3301 nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpenerWindowOuter();
3302 return opener.forget();
3305 void nsGlobalWindowOuter::GetStatusOuter(nsAString& aStatus) {
3306 aStatus = mStatus;
3309 void nsGlobalWindowOuter::SetStatusOuter(const nsAString& aStatus) {
3310 mStatus = aStatus;
3312 // We don't support displaying window.status in the UI, so there's nothing
3313 // left to do here.
3316 void nsGlobalWindowOuter::GetNameOuter(nsAString& aName) {
3317 if (mDocShell) {
3318 mDocShell->GetName(aName);
3322 void nsGlobalWindowOuter::SetNameOuter(const nsAString& aName,
3323 mozilla::ErrorResult& aError) {
3324 if (mDocShell) {
3325 aError = mDocShell->SetName(aName);
3329 // Helper functions used by many methods below.
3330 int32_t nsGlobalWindowOuter::DevToCSSIntPixels(int32_t px) {
3331 if (!mDocShell) return px; // assume 1:1
3333 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3334 if (!presContext) return px;
3336 return presContext->DevPixelsToIntCSSPixels(px);
3339 int32_t nsGlobalWindowOuter::CSSToDevIntPixels(int32_t px) {
3340 if (!mDocShell) return px; // assume 1:1
3342 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3343 if (!presContext) return px;
3345 return presContext->CSSPixelsToDevPixels(px);
3348 nsIntSize nsGlobalWindowOuter::DevToCSSIntPixels(nsIntSize px) {
3349 if (!mDocShell) return px; // assume 1:1
3351 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3352 if (!presContext) return px;
3354 return nsIntSize(presContext->DevPixelsToIntCSSPixels(px.width),
3355 presContext->DevPixelsToIntCSSPixels(px.height));
3358 nsIntSize nsGlobalWindowOuter::CSSToDevIntPixels(nsIntSize px) {
3359 if (!mDocShell) return px; // assume 1:1
3361 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3362 if (!presContext) return px;
3364 return nsIntSize(presContext->CSSPixelsToDevPixels(px.width),
3365 presContext->CSSPixelsToDevPixels(px.height));
3368 nsresult nsGlobalWindowOuter::GetInnerSize(CSSIntSize& aSize) {
3369 EnsureSizeAndPositionUpToDate();
3371 NS_ENSURE_STATE(mDocShell);
3373 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3374 PresShell* presShell = mDocShell->GetPresShell();
3376 if (!presContext || !presShell) {
3377 aSize = CSSIntSize(0, 0);
3378 return NS_OK;
3381 // If the visual viewport has been overriden, return that.
3382 if (presShell->IsVisualViewportSizeSet()) {
3383 aSize = CSSIntRect::FromAppUnitsRounded(presShell->GetVisualViewportSize());
3384 return NS_OK;
3387 // Whether or not the css viewport has been overridden, we can get the
3388 // correct value by looking at the visible area of the presContext.
3389 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
3390 if (viewManager) {
3391 viewManager->FlushDelayedResize(false);
3394 aSize = CSSIntRect::FromAppUnitsRounded(presContext->GetVisibleArea().Size());
3395 return NS_OK;
3398 int32_t nsGlobalWindowOuter::GetInnerWidthOuter(ErrorResult& aError) {
3399 CSSIntSize size;
3400 aError = GetInnerSize(size);
3401 return size.width;
3404 nsresult nsGlobalWindowOuter::GetInnerWidth(int32_t* aInnerWidth) {
3405 FORWARD_TO_INNER(GetInnerWidth, (aInnerWidth), NS_ERROR_UNEXPECTED);
3408 void nsGlobalWindowOuter::SetInnerWidthOuter(int32_t aInnerWidth,
3409 CallerType aCallerType,
3410 ErrorResult& aError) {
3411 if (!mDocShell) {
3412 aError.Throw(NS_ERROR_UNEXPECTED);
3413 return;
3416 CheckSecurityWidthAndHeight(&aInnerWidth, nullptr, aCallerType);
3417 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
3419 // Setting inner width should set the visual viewport. Most of the
3420 // time, this is the same as the CSS viewport, and when we set one,
3421 // we implicitly set both of them. But if
3422 // presShell->IsVisualViewportSizeSet() returns true, that means
3423 // that the two diverge. In that case we only set the visual viewport.
3424 // This mirrors the logic in ::GetInnerSize() and ensures that JS
3425 // behaves sanely when setting and then getting the innerWidth.
3426 if (presShell && presShell->IsVisualViewportSizeSet()) {
3427 CSSSize viewportSize =
3428 CSSRect::FromAppUnits(presShell->GetVisualViewportSize());
3429 viewportSize.width = aInnerWidth;
3430 nsLayoutUtils::SetVisualViewportSize(presShell, viewportSize);
3431 return;
3434 // We're going to set both viewports. If the css viewport has been
3435 // overridden, change the css viewport override. The visual viewport
3436 // will adopt this value via the logic in ::GetInnerSize().
3437 if (presShell && presShell->GetIsViewportOverridden()) {
3438 nscoord height = 0;
3440 RefPtr<nsPresContext> presContext;
3441 presContext = presShell->GetPresContext();
3443 nsRect shellArea = presContext->GetVisibleArea();
3444 height = shellArea.Height();
3445 SetCSSViewportWidthAndHeight(
3446 nsPresContext::CSSPixelsToAppUnits(aInnerWidth), height);
3447 return;
3450 // Nothing has been overriden, so change the docshell itself, which will
3451 // affect both viewports.
3452 int32_t height = 0;
3453 int32_t unused = 0;
3455 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
3456 docShellAsWin->GetSize(&unused, &height);
3457 aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height);
3460 int32_t nsGlobalWindowOuter::GetInnerHeightOuter(ErrorResult& aError) {
3461 CSSIntSize size;
3462 aError = GetInnerSize(size);
3463 return size.height;
3466 nsresult nsGlobalWindowOuter::GetInnerHeight(int32_t* aInnerHeight) {
3467 FORWARD_TO_INNER(GetInnerHeight, (aInnerHeight), NS_ERROR_UNEXPECTED);
3470 void nsGlobalWindowOuter::SetInnerHeightOuter(int32_t aInnerHeight,
3471 CallerType aCallerType,
3472 ErrorResult& aError) {
3473 if (!mDocShell) {
3474 aError.Throw(NS_ERROR_UNEXPECTED);
3475 return;
3478 CheckSecurityWidthAndHeight(nullptr, &aInnerHeight, aCallerType);
3479 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
3481 // Setting inner height should set the visual viewport. Most of the
3482 // time, this is the same as the CSS viewport, and when we set one,
3483 // we implicitly set both of them. But if
3484 // presShell->IsVisualViewportSizeSet() returns true, that means
3485 // that the two diverge. In that case we only set the visual viewport.
3486 // This mirrors the logic in ::GetInnerSize() and ensures that JS
3487 // behaves sanely when setting and then getting the innerHeight.
3488 if (presShell && presShell->IsVisualViewportSizeSet()) {
3489 CSSSize viewportSize =
3490 CSSRect::FromAppUnits(presShell->GetVisualViewportSize());
3491 viewportSize.height = aInnerHeight;
3492 nsLayoutUtils::SetVisualViewportSize(presShell, viewportSize);
3493 return;
3496 // We're going to set both viewports. If the css viewport has been
3497 // overridden, change the css viewport override. The visual viewport
3498 // will adopt this value via the logic in ::GetInnerSize().
3499 if (presShell && presShell->GetIsViewportOverridden()) {
3500 nscoord width = 0;
3502 RefPtr<nsPresContext> presContext;
3503 presContext = presShell->GetPresContext();
3505 nsRect shellArea = presContext->GetVisibleArea();
3506 width = shellArea.Width();
3507 SetCSSViewportWidthAndHeight(
3508 width, nsPresContext::CSSPixelsToAppUnits(aInnerHeight));
3509 return;
3512 // Nothing has been overriden, so change the docshell itself, which will
3513 // affect both viewports.
3514 int32_t height = 0;
3515 int32_t width = 0;
3517 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
3518 docShellAsWin->GetSize(&width, &height);
3519 aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
3522 nsIntSize nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType,
3523 ErrorResult& aError) {
3524 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3525 CSSIntSize size;
3526 aError = GetInnerSize(size);
3527 return nsIntSize(size.width, size.height);
3530 if (mDoc && mDoc->InRDMPane()) {
3531 CSSIntSize size;
3532 aError = GetInnerSize(size);
3534 // Obtain the current zoom of the presentation shell. The zoom value will
3535 // be used to scale the size of the visual viewport to the device browser's
3536 // outer size values. Once RDM no longer relies on the having the page
3537 // content being embedded in a <iframe mozbrowser>, we can do away with
3538 // this approach and retrieve the size of the frame containing the browser
3539 // content.
3540 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3542 if (presContext) {
3543 float zoom = presContext->GetDeviceFullZoom();
3544 int32_t width = std::round(size.width * zoom);
3545 int32_t height = std::round(size.height * zoom);
3546 return nsIntSize(width, height);
3550 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3551 if (!treeOwnerAsWin) {
3552 aError.Throw(NS_ERROR_FAILURE);
3553 return nsIntSize(0, 0);
3556 nsIntSize sizeDevPixels;
3557 aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
3558 if (aError.Failed()) {
3559 return nsIntSize();
3562 return DevToCSSIntPixels(sizeDevPixels);
3565 int32_t nsGlobalWindowOuter::GetOuterWidthOuter(CallerType aCallerType,
3566 ErrorResult& aError) {
3567 return GetOuterSize(aCallerType, aError).width;
3570 int32_t nsGlobalWindowOuter::GetOuterHeightOuter(CallerType aCallerType,
3571 ErrorResult& aError) {
3572 return GetOuterSize(aCallerType, aError).height;
3575 void nsGlobalWindowOuter::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
3576 CallerType aCallerType,
3577 ErrorResult& aError) {
3578 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3579 if (!treeOwnerAsWin) {
3580 aError.Throw(NS_ERROR_FAILURE);
3581 return;
3584 CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
3585 aIsWidth ? nullptr : &aLengthCSSPixels,
3586 aCallerType);
3588 int32_t width, height;
3589 aError = treeOwnerAsWin->GetSize(&width, &height);
3590 if (aError.Failed()) {
3591 return;
3594 int32_t lengthDevPixels = CSSToDevIntPixels(aLengthCSSPixels);
3595 if (aIsWidth) {
3596 width = lengthDevPixels;
3597 } else {
3598 height = lengthDevPixels;
3600 aError = treeOwnerAsWin->SetSize(width, height, true);
3602 CheckForDPIChange();
3605 void nsGlobalWindowOuter::SetOuterWidthOuter(int32_t aOuterWidth,
3606 CallerType aCallerType,
3607 ErrorResult& aError) {
3608 SetOuterSize(aOuterWidth, true, aCallerType, aError);
3611 void nsGlobalWindowOuter::SetOuterHeightOuter(int32_t aOuterHeight,
3612 CallerType aCallerType,
3613 ErrorResult& aError) {
3614 SetOuterSize(aOuterHeight, false, aCallerType, aError);
3617 CSSIntPoint nsGlobalWindowOuter::GetScreenXY(CallerType aCallerType,
3618 ErrorResult& aError) {
3619 // When resisting fingerprinting, always return (0,0)
3620 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3621 return CSSIntPoint(0, 0);
3624 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3625 if (!treeOwnerAsWin) {
3626 aError.Throw(NS_ERROR_FAILURE);
3627 return CSSIntPoint(0, 0);
3630 int32_t x = 0, y = 0;
3631 aError = treeOwnerAsWin->GetPosition(&x, &y); // LayoutDevice px values
3633 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3634 if (!presContext) {
3635 return CSSIntPoint(x, y);
3638 // Find the global desktop coordinate of the top-left of the screen.
3639 // We'll use this as a "fake origin" when converting to CSS px units,
3640 // to avoid overlapping coordinates in cases such as a hi-dpi screen
3641 // placed to the right of a lo-dpi screen on Windows. (Instead, there
3642 // may be "gaps" in the resulting CSS px coordinates in some cases.)
3643 nsDeviceContext* dc = presContext->DeviceContext();
3644 nsRect screenRect;
3645 dc->GetRect(screenRect);
3646 LayoutDeviceRect screenRectDev =
3647 LayoutDevicePixel::FromAppUnits(screenRect, dc->AppUnitsPerDevPixel());
3649 DesktopToLayoutDeviceScale scale = dc->GetDesktopToDeviceScale();
3650 DesktopRect screenRectDesk = screenRectDev / scale;
3652 CSSPoint cssPt = LayoutDevicePoint(x - screenRectDev.x, y - screenRectDev.y) /
3653 presContext->CSSToDevPixelScale();
3654 cssPt.x += screenRectDesk.x;
3655 cssPt.y += screenRectDesk.y;
3657 return CSSIntPoint(NSToIntRound(cssPt.x), NSToIntRound(cssPt.y));
3660 int32_t nsGlobalWindowOuter::GetScreenXOuter(CallerType aCallerType,
3661 ErrorResult& aError) {
3662 return GetScreenXY(aCallerType, aError).x;
3665 nsRect nsGlobalWindowOuter::GetInnerScreenRect() {
3666 if (!mDocShell) {
3667 return nsRect();
3670 EnsureSizeAndPositionUpToDate();
3672 if (!mDocShell) {
3673 return nsRect();
3676 PresShell* presShell = mDocShell->GetPresShell();
3677 if (!presShell) {
3678 return nsRect();
3680 nsIFrame* rootFrame = presShell->GetRootFrame();
3681 if (!rootFrame) {
3682 return nsRect();
3685 return rootFrame->GetScreenRectInAppUnits();
3688 float nsGlobalWindowOuter::GetMozInnerScreenXOuter(CallerType aCallerType) {
3689 // When resisting fingerprinting, always return 0.
3690 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3691 return 0.0;
3694 nsRect r = GetInnerScreenRect();
3695 return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
3698 float nsGlobalWindowOuter::GetMozInnerScreenYOuter(CallerType aCallerType) {
3699 // Return 0 to prevent fingerprinting.
3700 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3701 return 0.0;
3704 nsRect r = GetInnerScreenRect();
3705 return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
3708 double nsGlobalWindowOuter::GetDevicePixelRatioOuter(CallerType aCallerType) {
3709 if (!mDocShell) {
3710 return 1.0;
3713 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3714 if (!presContext) {
3715 return 1.0;
3718 if (nsContentUtils::ResistFingerprinting(aCallerType)) {
3719 return 1.0;
3722 float overrideDPPX = presContext->GetOverrideDPPX();
3724 if (overrideDPPX > 0) {
3725 return overrideDPPX;
3728 return double(AppUnitsPerCSSPixel()) /
3729 double(presContext->AppUnitsPerDevPixel());
3732 float nsPIDOMWindowOuter::GetDevicePixelRatio(CallerType aCallerType) {
3733 return nsGlobalWindowOuter::Cast(this)->GetDevicePixelRatioOuter(aCallerType);
3736 uint64_t nsGlobalWindowOuter::GetMozPaintCountOuter() {
3737 if (!mDocShell) {
3738 return 0;
3741 PresShell* presShell = mDocShell->GetPresShell();
3742 return presShell ? presShell->GetPaintCount() : 0;
3745 already_AddRefed<MediaQueryList> nsGlobalWindowOuter::MatchMediaOuter(
3746 const nsAString& aMediaQueryList, CallerType aCallerType) {
3747 if (!mDoc) {
3748 return nullptr;
3751 return mDoc->MatchMedia(aMediaQueryList, aCallerType);
3754 void nsGlobalWindowOuter::SetScreenXOuter(int32_t aScreenX,
3755 CallerType aCallerType,
3756 ErrorResult& aError) {
3757 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3758 if (!treeOwnerAsWin) {
3759 aError.Throw(NS_ERROR_FAILURE);
3760 return;
3763 int32_t x, y;
3764 aError = treeOwnerAsWin->GetPosition(&x, &y);
3765 if (aError.Failed()) {
3766 return;
3769 CheckSecurityLeftAndTop(&aScreenX, nullptr, aCallerType);
3770 x = CSSToDevIntPixels(aScreenX);
3772 aError = treeOwnerAsWin->SetPosition(x, y);
3774 CheckForDPIChange();
3777 int32_t nsGlobalWindowOuter::GetScreenYOuter(CallerType aCallerType,
3778 ErrorResult& aError) {
3779 return GetScreenXY(aCallerType, aError).y;
3782 void nsGlobalWindowOuter::SetScreenYOuter(int32_t aScreenY,
3783 CallerType aCallerType,
3784 ErrorResult& aError) {
3785 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
3786 if (!treeOwnerAsWin) {
3787 aError.Throw(NS_ERROR_FAILURE);
3788 return;
3791 int32_t x, y;
3792 aError = treeOwnerAsWin->GetPosition(&x, &y);
3793 if (aError.Failed()) {
3794 return;
3797 CheckSecurityLeftAndTop(nullptr, &aScreenY, aCallerType);
3798 y = CSSToDevIntPixels(aScreenY);
3800 aError = treeOwnerAsWin->SetPosition(x, y);
3802 CheckForDPIChange();
3805 // NOTE: Arguments to this function should have values scaled to
3806 // CSS pixels, not device pixels.
3807 void nsGlobalWindowOuter::CheckSecurityWidthAndHeight(int32_t* aWidth,
3808 int32_t* aHeight,
3809 CallerType aCallerType) {
3810 #ifdef MOZ_XUL
3811 if (aCallerType != CallerType::System) {
3812 // if attempting to resize the window, hide any open popups
3813 nsContentUtils::HidePopupsInDocument(mDoc);
3815 #endif
3817 // This one is easy. Just ensure the variable is greater than 100;
3818 if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
3819 // Check security state for use in determing window dimensions
3821 if (aCallerType != CallerType::System) {
3822 // sec check failed
3823 if (aWidth && *aWidth < 100) {
3824 *aWidth = 100;
3826 if (aHeight && *aHeight < 100) {
3827 *aHeight = 100;
3833 // NOTE: Arguments to this function should have values in device pixels
3834 nsresult nsGlobalWindowOuter::SetDocShellWidthAndHeight(int32_t aInnerWidth,
3835 int32_t aInnerHeight) {
3836 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
3838 nsCOMPtr<nsIDocShell> docShell = mDocShell;
3839 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
3840 docShell->GetTreeOwner(getter_AddRefs(treeOwner));
3841 NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
3843 NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(docShell, aInnerWidth, aInnerHeight),
3844 NS_ERROR_FAILURE);
3846 return NS_OK;
3849 // NOTE: Arguments to this function should have values in app units
3850 void nsGlobalWindowOuter::SetCSSViewportWidthAndHeight(nscoord aInnerWidth,
3851 nscoord aInnerHeight) {
3852 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
3854 nsRect shellArea = presContext->GetVisibleArea();
3855 shellArea.SetHeight(aInnerHeight);
3856 shellArea.SetWidth(aInnerWidth);
3858 presContext->SetVisibleArea(shellArea);
3861 // NOTE: Arguments to this function should have values scaled to
3862 // CSS pixels, not device pixels.
3863 void nsGlobalWindowOuter::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop,
3864 CallerType aCallerType) {
3865 // This one is harder. We have to get the screen size and window dimensions.
3867 // Check security state for use in determing window dimensions
3869 if (aCallerType != CallerType::System) {
3870 #ifdef MOZ_XUL
3871 // if attempting to move the window, hide any open popups
3872 nsContentUtils::HidePopupsInDocument(mDoc);
3873 #endif
3875 if (nsGlobalWindowOuter* rootWindow =
3876 nsGlobalWindowOuter::Cast(GetPrivateRoot())) {
3877 rootWindow->FlushPendingNotifications(FlushType::Layout);
3880 nsCOMPtr<nsIBaseWindow> treeOwner = GetTreeOwnerWindow();
3882 RefPtr<nsScreen> screen = GetScreen();
3884 if (treeOwner && screen) {
3885 int32_t winLeft, winTop, winWidth, winHeight;
3887 // Get the window size
3888 treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
3890 // convert those values to CSS pixels
3891 // XXX four separate retrievals of the prescontext
3892 winLeft = DevToCSSIntPixels(winLeft);
3893 winTop = DevToCSSIntPixels(winTop);
3894 winWidth = DevToCSSIntPixels(winWidth);
3895 winHeight = DevToCSSIntPixels(winHeight);
3897 // Get the screen dimensions
3898 // XXX This should use nsIScreenManager once it's fully fleshed out.
3899 int32_t screenLeft = screen->GetAvailLeft(IgnoreErrors());
3900 int32_t screenWidth = screen->GetAvailWidth(IgnoreErrors());
3901 int32_t screenHeight = screen->GetAvailHeight(IgnoreErrors());
3902 #if defined(XP_MACOSX)
3903 /* The mac's coordinate system is different from the assumed Windows'
3904 system. It offsets by the height of the menubar so that a window
3905 placed at (0,0) will be entirely visible. Unfortunately that
3906 correction is made elsewhere (in Widget) and the meaning of
3907 the Avail... coordinates is overloaded. Here we allow a window
3908 to be placed at (0,0) because it does make sense to do so.
3910 int32_t screenTop = screen->GetTop(IgnoreErrors());
3911 #else
3912 int32_t screenTop = screen->GetAvailTop(IgnoreErrors());
3913 #endif
3915 if (aLeft) {
3916 if (screenLeft + screenWidth < *aLeft + winWidth)
3917 *aLeft = screenLeft + screenWidth - winWidth;
3918 if (screenLeft > *aLeft) *aLeft = screenLeft;
3920 if (aTop) {
3921 if (screenTop + screenHeight < *aTop + winHeight)
3922 *aTop = screenTop + screenHeight - winHeight;
3923 if (screenTop > *aTop) *aTop = screenTop;
3925 } else {
3926 if (aLeft) *aLeft = 0;
3927 if (aTop) *aTop = 0;
3932 int32_t nsGlobalWindowOuter::GetScrollBoundaryOuter(Side aSide) {
3933 FlushPendingNotifications(FlushType::Layout);
3934 if (nsIScrollableFrame* sf = GetScrollFrame()) {
3935 return nsPresContext::AppUnitsToIntCSSPixels(
3936 sf->GetScrollRange().Edge(aSide));
3938 return 0;
3941 CSSPoint nsGlobalWindowOuter::GetScrollXY(bool aDoFlush) {
3942 if (aDoFlush) {
3943 FlushPendingNotifications(FlushType::Layout);
3944 } else {
3945 EnsureSizeAndPositionUpToDate();
3948 nsIScrollableFrame* sf = GetScrollFrame();
3949 if (!sf) {
3950 return CSSIntPoint(0, 0);
3953 nsPoint scrollPos = sf->GetScrollPosition();
3954 if (scrollPos != nsPoint(0, 0) && !aDoFlush) {
3955 // Oh, well. This is the expensive case -- the window is scrolled and we
3956 // didn't actually flush yet. Repeat, but with a flush, since the content
3957 // may get shorter and hence our scroll position may decrease.
3958 return GetScrollXY(true);
3961 return CSSPoint::FromAppUnits(scrollPos);
3964 double nsGlobalWindowOuter::GetScrollXOuter() { return GetScrollXY(false).x; }
3966 double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
3968 uint32_t nsGlobalWindowOuter::Length() {
3969 nsDOMWindowList* windows = GetFrames();
3971 return windows ? windows->GetLength() : 0;
3974 Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {
3975 BrowsingContext* bc = GetBrowsingContext();
3976 return bc ? bc->GetTop(IgnoreErrors()) : nullptr;
3979 already_AddRefed<BrowsingContext> nsGlobalWindowOuter::GetChildWindow(
3980 const nsAString& aName) {
3981 NS_ENSURE_TRUE(mBrowsingContext, nullptr);
3983 return do_AddRef(mBrowsingContext->FindChildWithName(aName));
3986 bool nsGlobalWindowOuter::DispatchCustomEvent(const nsAString& aEventName) {
3987 bool defaultActionEnabled = true;
3988 nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
3989 CanBubble::eYes, Cancelable::eYes,
3990 &defaultActionEnabled);
3992 return defaultActionEnabled;
3995 bool nsGlobalWindowOuter::DispatchResizeEvent(const CSSIntSize& aSize) {
3996 ErrorResult res;
3997 RefPtr<Event> domEvent = mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"),
3998 CallerType::System, res);
3999 if (res.Failed()) {
4000 return false;
4003 // We don't init the AutoJSAPI with ourselves because we don't want it
4004 // reporting errors to our onerror handlers.
4005 AutoJSAPI jsapi;
4006 jsapi.Init();
4007 JSContext* cx = jsapi.cx();
4008 JSAutoRealm ar(cx, GetWrapperPreserveColor());
4010 DOMWindowResizeEventDetail detail;
4011 detail.mWidth = aSize.width;
4012 detail.mHeight = aSize.height;
4013 JS::Rooted<JS::Value> detailValue(cx);
4014 if (!ToJSValue(cx, detail, &detailValue)) {
4015 return false;
4018 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
4019 customEvent->InitCustomEvent(cx, NS_LITERAL_STRING("DOMWindowResize"),
4020 /* aCanBubble = */ true,
4021 /* aCancelable = */ true, detailValue);
4023 domEvent->SetTrusted(true);
4024 domEvent->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
4026 nsCOMPtr<EventTarget> target = this;
4027 domEvent->SetTarget(target);
4029 return target->DispatchEvent(*domEvent, CallerType::System, IgnoreErrors());
4032 static already_AddRefed<nsIDocShellTreeItem> GetCallerDocShellTreeItem() {
4033 nsCOMPtr<nsIWebNavigation> callerWebNav = do_GetInterface(GetEntryGlobal());
4034 nsCOMPtr<nsIDocShellTreeItem> callerItem = do_QueryInterface(callerWebNav);
4036 return callerItem.forget();
4039 bool nsGlobalWindowOuter::WindowExists(const nsAString& aName,
4040 bool aForceNoOpener,
4041 bool aLookForCallerOnJSStack) {
4042 MOZ_ASSERT(mDocShell, "Must have docshell");
4044 if (aForceNoOpener) {
4045 return aName.LowerCaseEqualsLiteral("_self") ||
4046 aName.LowerCaseEqualsLiteral("_top") ||
4047 aName.LowerCaseEqualsLiteral("_parent");
4050 nsCOMPtr<nsIDocShellTreeItem> caller;
4051 if (aLookForCallerOnJSStack) {
4052 caller = GetCallerDocShellTreeItem();
4055 if (!caller) {
4056 caller = mDocShell;
4059 nsCOMPtr<nsIDocShellTreeItem> namedItem;
4060 mDocShell->FindItemWithName(aName, nullptr, caller,
4061 /* aSkipTabGroup = */ false,
4062 getter_AddRefs(namedItem));
4063 return namedItem != nullptr;
4066 already_AddRefed<nsIWidget> nsGlobalWindowOuter::GetMainWidget() {
4067 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4069 nsCOMPtr<nsIWidget> widget;
4071 if (treeOwnerAsWin) {
4072 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
4075 return widget.forget();
4078 nsIWidget* nsGlobalWindowOuter::GetNearestWidget() const {
4079 nsIDocShell* docShell = GetDocShell();
4080 NS_ENSURE_TRUE(docShell, nullptr);
4081 PresShell* presShell = docShell->GetPresShell();
4082 NS_ENSURE_TRUE(presShell, nullptr);
4083 nsIFrame* rootFrame = presShell->GetRootFrame();
4084 NS_ENSURE_TRUE(rootFrame, nullptr);
4085 return rootFrame->GetView()->GetNearestWidget(nullptr);
4088 void nsGlobalWindowOuter::SetFullscreenOuter(bool aFullscreen,
4089 mozilla::ErrorResult& aError) {
4090 aError =
4091 SetFullscreenInternal(FullscreenReason::ForFullscreenMode, aFullscreen);
4094 nsresult nsGlobalWindowOuter::SetFullScreen(bool aFullscreen) {
4095 return SetFullscreenInternal(FullscreenReason::ForFullscreenMode,
4096 aFullscreen);
4099 static void FinishDOMFullscreenChange(Document* aDoc, bool aInDOMFullscreen) {
4100 if (aInDOMFullscreen) {
4101 // Ask the document to handle any pending DOM fullscreen change.
4102 if (!Document::HandlePendingFullscreenRequests(aDoc)) {
4103 // If we don't end up having anything in fullscreen,
4104 // async request exiting fullscreen.
4105 Document::AsyncExitFullscreen(aDoc);
4107 } else {
4108 // If the window is leaving fullscreen state, also ask the document
4109 // to exit from DOM Fullscreen.
4110 Document::ExitFullscreenInDocTree(aDoc);
4114 struct FullscreenTransitionDuration {
4115 // The unit of the durations is millisecond
4116 uint16_t mFadeIn = 0;
4117 uint16_t mFadeOut = 0;
4118 bool IsSuppressed() const { return mFadeIn == 0 && mFadeOut == 0; }
4121 static void GetFullscreenTransitionDuration(
4122 bool aEnterFullscreen, FullscreenTransitionDuration* aDuration) {
4123 const char* pref = aEnterFullscreen
4124 ? "full-screen-api.transition-duration.enter"
4125 : "full-screen-api.transition-duration.leave";
4126 nsAutoCString prefValue;
4127 Preferences::GetCString(pref, prefValue);
4128 if (!prefValue.IsEmpty()) {
4129 sscanf(prefValue.get(), "%hu%hu", &aDuration->mFadeIn,
4130 &aDuration->mFadeOut);
4134 class FullscreenTransitionTask : public Runnable {
4135 public:
4136 FullscreenTransitionTask(const FullscreenTransitionDuration& aDuration,
4137 nsGlobalWindowOuter* aWindow, bool aFullscreen,
4138 nsIWidget* aWidget, nsIScreen* aScreen,
4139 nsISupports* aTransitionData)
4140 : mozilla::Runnable("FullscreenTransitionTask"),
4141 mWindow(aWindow),
4142 mWidget(aWidget),
4143 mScreen(aScreen),
4144 mTransitionData(aTransitionData),
4145 mDuration(aDuration),
4146 mStage(eBeforeToggle),
4147 mFullscreen(aFullscreen) {}
4149 NS_IMETHOD Run() override;
4151 private:
4152 ~FullscreenTransitionTask() override {}
4155 * The flow of fullscreen transition:
4157 * parent process | child process
4158 * ----------------------------------------------------------------
4160 * | request/exit fullscreen
4161 * <-----|
4162 * BeforeToggle stage |
4164 * ToggleFullscreen stage *1 |----->
4165 * | HandleFullscreenRequests
4167 * <-----| MozAfterPaint event
4168 * AfterToggle stage *2 |
4170 * End stage |
4172 * Note we also start a timer at *1 so that if we don't get MozAfterPaint
4173 * from the child process in time, we continue going to *2.
4175 enum Stage {
4176 // BeforeToggle stage happens before we enter or leave fullscreen
4177 // state. In this stage, the task triggers the pre-toggle fullscreen
4178 // transition on the widget.
4179 eBeforeToggle,
4180 // ToggleFullscreen stage actually executes the fullscreen toggle,
4181 // and wait for the next paint on the content to continue.
4182 eToggleFullscreen,
4183 // AfterToggle stage happens after we toggle the fullscreen state.
4184 // In this stage, the task triggers the post-toggle fullscreen
4185 // transition on the widget.
4186 eAfterToggle,
4187 // End stage is triggered after the final transition finishes.
4188 eEnd
4191 class Observer final : public nsIObserver {
4192 public:
4193 NS_DECL_ISUPPORTS
4194 NS_DECL_NSIOBSERVER
4196 explicit Observer(FullscreenTransitionTask* aTask) : mTask(aTask) {}
4198 private:
4199 ~Observer() = default;
4201 RefPtr<FullscreenTransitionTask> mTask;
4204 static const char* const kPaintedTopic;
4206 RefPtr<nsGlobalWindowOuter> mWindow;
4207 nsCOMPtr<nsIWidget> mWidget;
4208 nsCOMPtr<nsIScreen> mScreen;
4209 nsCOMPtr<nsITimer> mTimer;
4210 nsCOMPtr<nsISupports> mTransitionData;
4212 TimeStamp mFullscreenChangeStartTime;
4213 FullscreenTransitionDuration mDuration;
4214 Stage mStage;
4215 bool mFullscreen;
4218 const char* const FullscreenTransitionTask::kPaintedTopic =
4219 "fullscreen-painted";
4221 NS_IMETHODIMP
4222 FullscreenTransitionTask::Run() {
4223 Stage stage = mStage;
4224 mStage = Stage(mStage + 1);
4225 if (MOZ_UNLIKELY(mWidget->Destroyed())) {
4226 // If the widget has been destroyed before we get here, don't try to
4227 // do anything more. Just let it go and release ourselves.
4228 NS_WARNING("The widget to fullscreen has been destroyed");
4229 return NS_OK;
4231 if (stage == eBeforeToggle) {
4232 PROFILER_ADD_MARKER("Fullscreen transition start", DOM);
4233 mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
4234 mDuration.mFadeIn, mTransitionData,
4235 this);
4236 } else if (stage == eToggleFullscreen) {
4237 PROFILER_ADD_MARKER("Fullscreen toggle start", DOM);
4238 mFullscreenChangeStartTime = TimeStamp::Now();
4239 if (MOZ_UNLIKELY(mWindow->mFullscreen != mFullscreen)) {
4240 // This could happen in theory if several fullscreen requests in
4241 // different direction happen continuously in a short time. We
4242 // need to ensure the fullscreen state matches our target here,
4243 // otherwise the widget would change the window state as if we
4244 // toggle for Fullscreen Mode instead of Fullscreen API.
4245 NS_WARNING("The fullscreen state of the window does not match");
4246 mWindow->mFullscreen = mFullscreen;
4248 // Toggle the fullscreen state on the widget
4249 if (!mWindow->SetWidgetFullscreen(FullscreenReason::ForFullscreenAPI,
4250 mFullscreen, mWidget, mScreen)) {
4251 // Fail to setup the widget, call FinishFullscreenChange to
4252 // complete fullscreen change directly.
4253 mWindow->FinishFullscreenChange(mFullscreen);
4255 // Set observer for the next content paint.
4256 nsCOMPtr<nsIObserver> observer = new Observer(this);
4257 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4258 obs->AddObserver(observer, kPaintedTopic, false);
4259 // There are several edge cases where we may never get the paint
4260 // notification, including:
4261 // 1. the window/tab is closed before the next paint;
4262 // 2. the user has switched to another tab before we get here.
4263 // Completely fixing those cases seems to be tricky, and since they
4264 // should rarely happen, it probably isn't worth to fix. Hence we
4265 // simply add a timeout here to ensure we never hang forever.
4266 // In addition, if the page is complicated or the machine is less
4267 // powerful, layout could take a long time, in which case, staying
4268 // in black screen for that long could hurt user experience even
4269 // more than exposing an intermediate state.
4270 uint32_t timeout =
4271 Preferences::GetUint("full-screen-api.transition.timeout", 1000);
4272 NS_NewTimerWithObserver(getter_AddRefs(mTimer), observer, timeout,
4273 nsITimer::TYPE_ONE_SHOT);
4274 } else if (stage == eAfterToggle) {
4275 Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
4276 mFullscreenChangeStartTime);
4277 mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
4278 mDuration.mFadeOut, mTransitionData,
4279 this);
4280 } else if (stage == eEnd) {
4281 PROFILER_ADD_MARKER("Fullscreen transition end", DOM);
4282 mWidget->CleanupFullscreenTransition();
4284 return NS_OK;
4287 NS_IMPL_ISUPPORTS(FullscreenTransitionTask::Observer, nsIObserver)
4289 NS_IMETHODIMP
4290 FullscreenTransitionTask::Observer::Observe(nsISupports* aSubject,
4291 const char* aTopic,
4292 const char16_t* aData) {
4293 bool shouldContinue = false;
4294 if (strcmp(aTopic, FullscreenTransitionTask::kPaintedTopic) == 0) {
4295 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aSubject));
4296 nsCOMPtr<nsIWidget> widget =
4297 win ? nsGlobalWindowInner::Cast(win)->GetMainWidget() : nullptr;
4298 if (widget == mTask->mWidget) {
4299 // The paint notification arrives first. Cancel the timer.
4300 mTask->mTimer->Cancel();
4301 shouldContinue = true;
4302 PROFILER_ADD_MARKER("Fullscreen toggle end", DOM);
4304 } else {
4305 #ifdef DEBUG
4306 MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0,
4307 "Should only get fullscreen-painted or timer-callback");
4308 nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
4309 MOZ_ASSERT(timer && timer == mTask->mTimer,
4310 "Should only trigger this with the timer the task created");
4311 #endif
4312 shouldContinue = true;
4313 PROFILER_ADD_MARKER("Fullscreen toggle timeout", DOM);
4315 if (shouldContinue) {
4316 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
4317 obs->RemoveObserver(this, kPaintedTopic);
4318 mTask->mTimer = nullptr;
4319 mTask->Run();
4321 return NS_OK;
4324 static bool MakeWidgetFullscreen(nsGlobalWindowOuter* aWindow,
4325 FullscreenReason aReason, bool aFullscreen) {
4326 nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget();
4327 if (!widget) {
4328 return false;
4331 FullscreenTransitionDuration duration;
4332 bool performTransition = false;
4333 nsCOMPtr<nsISupports> transitionData;
4334 if (aReason == FullscreenReason::ForFullscreenAPI) {
4335 GetFullscreenTransitionDuration(aFullscreen, &duration);
4336 if (!duration.IsSuppressed()) {
4337 performTransition = widget->PrepareForFullscreenTransition(
4338 getter_AddRefs(transitionData));
4341 // We pass nullptr as the screen to SetWidgetFullscreen
4342 // and FullscreenTransitionTask, as we do not wish to override
4343 // the default screen selection behavior. The screen containing
4344 // most of the widget will be selected.
4345 if (!performTransition) {
4346 return aWindow->SetWidgetFullscreen(aReason, aFullscreen, widget, nullptr);
4348 nsCOMPtr<nsIRunnable> task = new FullscreenTransitionTask(
4349 duration, aWindow, aFullscreen, widget, nullptr, transitionData);
4350 task->Run();
4351 return true;
4354 nsresult nsGlobalWindowOuter::SetFullscreenInternal(FullscreenReason aReason,
4355 bool aFullscreen) {
4356 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
4357 "Requires safe to run script as it "
4358 "may call FinishDOMFullscreenChange");
4360 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
4362 MOZ_ASSERT(
4363 aReason != FullscreenReason::ForForceExitFullscreen || !aFullscreen,
4364 "FullscreenReason::ForForceExitFullscreen can "
4365 "only be used with exiting fullscreen");
4367 // Only chrome can change our fullscreen mode. Otherwise, the state
4368 // can only be changed for DOM fullscreen.
4369 if (aReason == FullscreenReason::ForFullscreenMode &&
4370 !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
4371 return NS_OK;
4374 // SetFullscreen needs to be called on the root window, so get that
4375 // via the DocShell tree, and if we are not already the root,
4376 // call SetFullscreen on that window instead.
4377 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4378 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
4379 nsCOMPtr<nsPIDOMWindowOuter> window =
4380 rootItem ? rootItem->GetWindow() : nullptr;
4381 if (!window) return NS_ERROR_FAILURE;
4382 if (rootItem != mDocShell)
4383 return window->SetFullscreenInternal(aReason, aFullscreen);
4385 // make sure we don't try to set full screen on a non-chrome window,
4386 // which might happen in embedding world
4387 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome)
4388 return NS_ERROR_FAILURE;
4390 // If we are already in full screen mode, just return.
4391 if (mFullscreen == aFullscreen) {
4392 return NS_OK;
4395 // Note that although entering DOM fullscreen could also cause
4396 // consequential calls to this method, those calls will be skipped
4397 // at the condition above.
4398 if (aReason == FullscreenReason::ForFullscreenMode) {
4399 if (!aFullscreen && !mFullscreenMode) {
4400 // If we are exiting fullscreen mode, but we actually didn't
4401 // entered fullscreen mode, the fullscreen state was only for
4402 // the Fullscreen API. Change the reason here so that we can
4403 // perform transition for it.
4404 aReason = FullscreenReason::ForFullscreenAPI;
4405 } else {
4406 mFullscreenMode = aFullscreen;
4408 } else {
4409 // If we are exiting from DOM fullscreen while we initially make
4410 // the window fullscreen because of fullscreen mode, don't restore
4411 // the window. But we still need to exit the DOM fullscreen state.
4412 if (!aFullscreen && mFullscreenMode) {
4413 FinishDOMFullscreenChange(mDoc, false);
4414 return NS_OK;
4418 // Prevent chrome documents which are still loading from resizing
4419 // the window after we set fullscreen mode.
4420 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4421 nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwnerAsWin));
4422 if (aFullscreen && xulWin) {
4423 xulWin->SetIntrinsicallySized(false);
4426 // Set this before so if widget sends an event indicating its
4427 // gone full screen, the state trap above works.
4428 mFullscreen = aFullscreen;
4430 // Sometimes we don't want the top-level widget to actually go fullscreen,
4431 // for example in the B2G desktop client, we don't want the emulated screen
4432 // dimensions to appear to increase when entering fullscreen mode; we just
4433 // want the content to fill the entire client area of the emulator window.
4434 if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) {
4435 if (MakeWidgetFullscreen(this, aReason, aFullscreen)) {
4436 // The rest of code for switching fullscreen is in nsGlobalWindowOuter::
4437 // FinishFullscreenChange() which will be called after sizemodechange
4438 // event is dispatched.
4439 return NS_OK;
4443 FinishFullscreenChange(aFullscreen);
4444 return NS_OK;
4447 bool nsGlobalWindowOuter::SetWidgetFullscreen(FullscreenReason aReason,
4448 bool aIsFullscreen,
4449 nsIWidget* aWidget,
4450 nsIScreen* aScreen) {
4451 MOZ_ASSERT(this == GetTopInternal(), "Only topmost window should call this");
4452 MOZ_ASSERT(!GetFrameElementInternal(), "Content window should not call this");
4453 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
4455 if (!NS_WARN_IF(!IsChromeWindow())) {
4456 if (!NS_WARN_IF(mChromeFields.mFullscreenPresShell)) {
4457 if (PresShell* presShell = mDocShell->GetPresShell()) {
4458 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
4459 mChromeFields.mFullscreenPresShell = do_GetWeakReference(presShell);
4460 MOZ_ASSERT(mChromeFields.mFullscreenPresShell);
4461 rd->SetIsResizeSuppressed();
4462 rd->Freeze();
4467 nsresult rv =
4468 aReason == FullscreenReason::ForFullscreenMode
4470 // If we enter fullscreen for fullscreen mode, we want
4471 // the native system behavior.
4472 aWidget->MakeFullScreenWithNativeTransition(aIsFullscreen, aScreen)
4473 : aWidget->MakeFullScreen(aIsFullscreen, aScreen);
4474 return NS_SUCCEEDED(rv);
4477 /* virtual */
4478 void nsGlobalWindowOuter::FullscreenWillChange(bool aIsFullscreen) {
4479 if (aIsFullscreen) {
4480 DispatchCustomEvent(NS_LITERAL_STRING("willenterfullscreen"));
4481 } else {
4482 DispatchCustomEvent(NS_LITERAL_STRING("willexitfullscreen"));
4486 /* virtual */
4487 void nsGlobalWindowOuter::FinishFullscreenChange(bool aIsFullscreen) {
4488 if (aIsFullscreen != mFullscreen) {
4489 NS_WARNING("Failed to toggle fullscreen state of the widget");
4490 // We failed to make the widget enter fullscreen.
4491 // Stop further changes and restore the state.
4492 if (!aIsFullscreen) {
4493 mFullscreen = false;
4494 mFullscreenMode = false;
4495 } else {
4496 MOZ_ASSERT_UNREACHABLE("Failed to exit fullscreen?");
4497 mFullscreen = true;
4498 // We don't know how code can reach here. Not sure
4499 // what value should be set for fullscreen mode.
4500 mFullscreenMode = false;
4502 return;
4505 // Note that we must call this to toggle the DOM fullscreen state
4506 // of the document before dispatching the "fullscreen" event, so
4507 // that the chrome can distinguish between browser fullscreen mode
4508 // and DOM fullscreen.
4509 FinishDOMFullscreenChange(mDoc, mFullscreen);
4511 // dispatch a "fullscreen" DOM event so that XUL apps can
4512 // respond visually if we are kicked into full screen mode
4513 DispatchCustomEvent(NS_LITERAL_STRING("fullscreen"));
4515 if (!NS_WARN_IF(!IsChromeWindow())) {
4516 if (RefPtr<PresShell> presShell =
4517 do_QueryReferent(mChromeFields.mFullscreenPresShell)) {
4518 if (nsRefreshDriver* rd = presShell->GetRefreshDriver()) {
4519 rd->Thaw();
4521 mChromeFields.mFullscreenPresShell = nullptr;
4525 if (!mWakeLock && mFullscreen) {
4526 RefPtr<power::PowerManagerService> pmService =
4527 power::PowerManagerService::GetInstance();
4528 if (!pmService) {
4529 return;
4532 // XXXkhuey using the inner here, do we need to do something if it changes?
4533 ErrorResult rv;
4534 mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"),
4535 GetCurrentInnerWindow(), rv);
4536 NS_WARNING_ASSERTION(!rv.Failed(), "Failed to lock the wakelock");
4537 rv.SuppressException();
4538 } else if (mWakeLock && !mFullscreen) {
4539 ErrorResult rv;
4540 mWakeLock->Unlock(rv);
4541 mWakeLock = nullptr;
4542 rv.SuppressException();
4546 bool nsGlobalWindowOuter::Fullscreen() const {
4547 NS_ENSURE_TRUE(mDocShell, mFullscreen);
4549 // Get the fullscreen value of the root window, to always have the value
4550 // accurate, even when called from content.
4551 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4552 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
4553 if (rootItem == mDocShell) {
4554 if (!XRE_IsContentProcess()) {
4555 // We are the root window. Return our internal value.
4556 return mFullscreen;
4558 if (nsCOMPtr<nsIWidget> widget = GetNearestWidget()) {
4559 // We are in content process, figure out the value from
4560 // the sizemode of the puppet widget.
4561 return widget->SizeMode() == nsSizeMode_Fullscreen;
4563 return false;
4566 nsCOMPtr<nsPIDOMWindowOuter> window = rootItem->GetWindow();
4567 NS_ENSURE_TRUE(window, mFullscreen);
4569 return nsGlobalWindowOuter::Cast(window)->Fullscreen();
4572 bool nsGlobalWindowOuter::GetFullscreenOuter() { return Fullscreen(); }
4574 bool nsGlobalWindowOuter::GetFullScreen() {
4575 FORWARD_TO_INNER(GetFullScreen, (), false);
4578 void nsGlobalWindowOuter::EnsureReflowFlushAndPaint() {
4579 NS_ASSERTION(mDocShell,
4580 "EnsureReflowFlushAndPaint() called with no "
4581 "docshell!");
4583 if (!mDocShell) return;
4585 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
4586 if (!presShell) {
4587 return;
4590 // Flush pending reflows.
4591 if (mDoc) {
4592 mDoc->FlushPendingNotifications(FlushType::Layout);
4595 // Unsuppress painting.
4596 presShell->UnsuppressPainting();
4599 // static
4600 void nsGlobalWindowOuter::MakeScriptDialogTitle(
4601 nsAString& aOutTitle, nsIPrincipal* aSubjectPrincipal) {
4602 MOZ_ASSERT(aSubjectPrincipal);
4604 aOutTitle.Truncate();
4606 // Try to get a host from the running principal -- this will do the
4607 // right thing for javascript: and data: documents.
4609 nsCOMPtr<nsIURI> uri;
4610 nsresult rv = aSubjectPrincipal->GetURI(getter_AddRefs(uri));
4611 if (NS_SUCCEEDED(rv) && uri) {
4612 // remove user:pass for privacy and spoof prevention
4614 nsCOMPtr<nsIURIFixup> fixup(components::URIFixup::Service());
4615 if (fixup) {
4616 nsCOMPtr<nsIURI> fixedURI;
4617 rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
4618 if (NS_SUCCEEDED(rv) && fixedURI) {
4619 nsAutoCString host;
4620 fixedURI->GetHost(host);
4622 if (!host.IsEmpty()) {
4623 // if this URI has a host we'll show it. For other
4624 // schemes (e.g. file:) we fall back to the localized
4625 // generic string
4627 nsAutoCString prepath;
4628 fixedURI->GetDisplayPrePath(prepath);
4630 NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
4631 const char16_t* formatStrings[] = {ucsPrePath.get()};
4632 nsContentUtils::FormatLocalizedString(
4633 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDlgHeading",
4634 formatStrings, aOutTitle);
4640 if (aOutTitle.IsEmpty()) {
4641 // We didn't find a host so use the generic heading
4642 nsContentUtils::GetLocalizedString(
4643 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDlgGenericHeading",
4644 aOutTitle);
4647 // Just in case
4648 if (aOutTitle.IsEmpty()) {
4649 NS_WARNING(
4650 "could not get ScriptDlgGenericHeading string from string bundle");
4651 aOutTitle.AssignLiteral("[Script]");
4655 bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType) {
4656 // When called from chrome, we can avoid the following checks.
4657 if (aCallerType != CallerType::System) {
4658 // Don't allow scripts to move or resize windows that were not opened by a
4659 // script.
4660 if (!mHadOriginalOpener) {
4661 return false;
4664 if (!CanSetProperty("dom.disable_window_move_resize")) {
4665 return false;
4668 // Ignore the request if we have more than one tab in the window.
4669 if (XRE_IsContentProcess()) {
4670 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
4671 if (docShell) {
4672 nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild();
4673 bool hasSiblings = true;
4674 if (child && NS_SUCCEEDED(child->GetHasSiblings(&hasSiblings)) &&
4675 hasSiblings) {
4676 return false;
4679 } else {
4680 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
4681 uint32_t itemCount = 0;
4682 if (treeOwner && NS_SUCCEEDED(treeOwner->GetTabCount(&itemCount)) &&
4683 itemCount > 1) {
4684 return false;
4689 if (mDocShell) {
4690 bool allow;
4691 nsresult rv = mDocShell->GetAllowWindowControl(&allow);
4692 if (NS_SUCCEEDED(rv) && !allow) return false;
4695 if (nsGlobalWindowInner::sMouseDown &&
4696 !nsGlobalWindowInner::sDragServiceDisabled) {
4697 nsCOMPtr<nsIDragService> ds =
4698 do_GetService("@mozilla.org/widget/dragservice;1");
4699 if (ds) {
4700 nsGlobalWindowInner::sDragServiceDisabled = true;
4701 ds->Suppress();
4704 return true;
4707 bool nsGlobalWindowOuter::AlertOrConfirm(bool aAlert, const nsAString& aMessage,
4708 nsIPrincipal& aSubjectPrincipal,
4709 ErrorResult& aError) {
4710 // XXX This method is very similar to nsGlobalWindowOuter::Prompt, make
4711 // sure any modifications here don't need to happen over there!
4712 if (!AreDialogsEnabled()) {
4713 // Just silently return. In the case of alert(), the return value is
4714 // ignored. In the case of confirm(), returning false is the same thing as
4715 // would happen if the user cancels.
4716 return false;
4719 // Reset popup state while opening a modal dialog, and firing events
4720 // about the dialog, to prevent the current state from being active
4721 // the whole time a modal dialog is open.
4722 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
4724 // Before bringing up the window, unsuppress painting and flush
4725 // pending reflows.
4726 EnsureReflowFlushAndPaint();
4728 nsAutoString title;
4729 MakeScriptDialogTitle(title, &aSubjectPrincipal);
4731 // Remove non-terminating null characters from the
4732 // string. See bug #310037.
4733 nsAutoString final;
4734 nsContentUtils::StripNullChars(aMessage, final);
4735 nsContentUtils::PlatformToDOMLineBreaks(final);
4737 nsresult rv;
4738 nsCOMPtr<nsIPromptFactory> promptFac =
4739 do_GetService("@mozilla.org/prompter;1", &rv);
4740 if (NS_FAILED(rv)) {
4741 aError.Throw(rv);
4742 return false;
4745 nsCOMPtr<nsIPrompt> prompt;
4746 aError =
4747 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
4748 if (aError.Failed()) {
4749 return false;
4752 // Always allow tab modal prompts for alert and confirm.
4753 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
4754 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
4757 bool result = false;
4758 nsAutoSyncOperation sync(mDoc);
4759 if (ShouldPromptToBlockDialogs()) {
4760 bool disallowDialog = false;
4761 nsAutoString label;
4762 nsContentUtils::GetLocalizedString(
4763 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label);
4765 aError = aAlert
4766 ? prompt->AlertCheck(title.get(), final.get(), label.get(),
4767 &disallowDialog)
4768 : prompt->ConfirmCheck(title.get(), final.get(), label.get(),
4769 &disallowDialog, &result);
4771 if (disallowDialog) DisableDialogs();
4772 } else {
4773 aError = aAlert ? prompt->Alert(title.get(), final.get())
4774 : prompt->Confirm(title.get(), final.get(), &result);
4777 return result;
4780 void nsGlobalWindowOuter::AlertOuter(const nsAString& aMessage,
4781 nsIPrincipal& aSubjectPrincipal,
4782 ErrorResult& aError) {
4783 AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
4786 bool nsGlobalWindowOuter::ConfirmOuter(const nsAString& aMessage,
4787 nsIPrincipal& aSubjectPrincipal,
4788 ErrorResult& aError) {
4789 return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
4790 aError);
4793 void nsGlobalWindowOuter::PromptOuter(const nsAString& aMessage,
4794 const nsAString& aInitial,
4795 nsAString& aReturn,
4796 nsIPrincipal& aSubjectPrincipal,
4797 ErrorResult& aError) {
4798 // XXX This method is very similar to nsGlobalWindowOuter::AlertOrConfirm,
4799 // make sure any modifications here don't need to happen over there!
4800 SetDOMStringToNull(aReturn);
4802 if (!AreDialogsEnabled()) {
4803 // Return null, as if the user just canceled the prompt.
4804 return;
4807 // Reset popup state while opening a modal dialog, and firing events
4808 // about the dialog, to prevent the current state from being active
4809 // the whole time a modal dialog is open.
4810 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
4812 // Before bringing up the window, unsuppress painting and flush
4813 // pending reflows.
4814 EnsureReflowFlushAndPaint();
4816 nsAutoString title;
4817 MakeScriptDialogTitle(title, &aSubjectPrincipal);
4819 // Remove non-terminating null characters from the
4820 // string. See bug #310037.
4821 nsAutoString fixedMessage, fixedInitial;
4822 nsContentUtils::StripNullChars(aMessage, fixedMessage);
4823 nsContentUtils::PlatformToDOMLineBreaks(fixedMessage);
4824 nsContentUtils::StripNullChars(aInitial, fixedInitial);
4826 nsresult rv;
4827 nsCOMPtr<nsIPromptFactory> promptFac =
4828 do_GetService("@mozilla.org/prompter;1", &rv);
4829 if (NS_FAILED(rv)) {
4830 aError.Throw(rv);
4831 return;
4834 nsCOMPtr<nsIPrompt> prompt;
4835 aError =
4836 promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
4837 if (aError.Failed()) {
4838 return;
4841 // Always allow tab modal prompts for prompt.
4842 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
4843 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
4846 // Pass in the default value, if any.
4847 char16_t* inoutValue = ToNewUnicode(fixedInitial);
4848 bool disallowDialog = false;
4850 nsAutoString label;
4851 label.SetIsVoid(true);
4852 if (ShouldPromptToBlockDialogs()) {
4853 nsContentUtils::GetLocalizedString(
4854 nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label);
4857 nsAutoSyncOperation sync(mDoc);
4858 bool ok;
4859 aError = prompt->Prompt(title.get(), fixedMessage.get(), &inoutValue,
4860 label.IsVoid() ? nullptr : label.get(),
4861 &disallowDialog, &ok);
4863 if (disallowDialog) {
4864 DisableDialogs();
4867 if (aError.Failed()) {
4868 return;
4871 nsString outValue;
4872 outValue.Adopt(inoutValue);
4874 if (ok && inoutValue) {
4875 aReturn.Assign(outValue);
4879 void nsGlobalWindowOuter::FocusOuter() {
4880 nsFocusManager* fm = nsFocusManager::GetFocusManager();
4881 if (!fm) {
4882 return;
4885 nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
4886 if (!baseWin) {
4887 return;
4890 nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
4891 nsPIDOMWindowOuter* callerOuter = caller ? caller->GetOuterWindow() : nullptr;
4892 nsCOMPtr<nsPIDOMWindowOuter> opener = GetOpener();
4894 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
4895 // window which opened us to raise us at times when popups are allowed
4896 // (bugs 355482 and 369306).
4897 bool canFocus = CanSetProperty("dom.disable_window_flip") ||
4898 (opener == callerOuter &&
4899 RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
4900 PopupBlocker::openBlocked);
4902 nsCOMPtr<mozIDOMWindowProxy> activeDOMWindow;
4903 fm->GetActiveWindow(getter_AddRefs(activeDOMWindow));
4905 nsCOMPtr<nsIDocShellTreeItem> rootItem;
4906 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
4907 nsCOMPtr<nsPIDOMWindowOuter> rootWin =
4908 rootItem ? rootItem->GetWindow() : nullptr;
4909 auto* activeWindow = nsPIDOMWindowOuter::From(activeDOMWindow);
4910 bool isActive = (rootWin == activeWindow);
4912 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4913 if (treeOwnerAsWin && (canFocus || isActive)) {
4914 bool isEnabled = true;
4915 if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
4916 NS_WARNING("Should not try to set the focus on a disabled window");
4917 return;
4920 // XXXndeakin not sure what this is for or if it should go somewhere else
4921 nsCOMPtr<nsIEmbeddingSiteWindow> embeddingWin(
4922 do_GetInterface(treeOwnerAsWin));
4923 if (embeddingWin) embeddingWin->SetFocus();
4926 if (!mDocShell) {
4927 return;
4930 // Don't look for a presshell if we're a root chrome window that's got
4931 // about:blank loaded. We don't want to focus our widget in that case.
4932 // XXXbz should we really be checking for IsInitialDocument() instead?
4933 nsCOMPtr<nsIDocShellTreeItem> parentDsti;
4934 mDocShell->GetParent(getter_AddRefs(parentDsti));
4936 // set the parent's current focus to the frame containing this window.
4937 nsCOMPtr<nsPIDOMWindowOuter> parent =
4938 parentDsti ? parentDsti->GetWindow() : nullptr;
4939 if (parent) {
4940 nsCOMPtr<Document> parentdoc = parent->GetDoc();
4941 if (!parentdoc) {
4942 return;
4945 if (Element* frame = parentdoc->FindContentForSubDocument(mDoc)) {
4946 nsContentUtils::RequestFrameFocus(*frame, canFocus);
4948 return;
4951 if (canFocus) {
4952 // if there is no parent, this must be a toplevel window, so raise the
4953 // window if canFocus is true. If this is a child process, the raise
4954 // window request will get forwarded to the parent by the puppet widget.
4955 DebugOnly<nsresult> rv = fm->SetActiveWindow(this);
4956 MOZ_ASSERT(NS_SUCCEEDED(rv),
4957 "SetActiveWindow only fails if passed null or a non-toplevel "
4958 "window, which is not the case here.");
4962 nsresult nsGlobalWindowOuter::Focus() {
4963 FORWARD_TO_INNER(Focus, (), NS_ERROR_UNEXPECTED);
4966 void nsGlobalWindowOuter::BlurOuter() {
4967 // If dom.disable_window_flip == true, then content should not be allowed
4968 // to call this function (this would allow popunders, bug 369306)
4969 if (!CanSetProperty("dom.disable_window_flip")) {
4970 return;
4973 // If embedding apps don't implement nsIEmbeddingSiteWindow, we
4974 // shouldn't throw exceptions to web content.
4976 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
4977 nsCOMPtr<nsIEmbeddingSiteWindow> siteWindow(do_GetInterface(treeOwner));
4978 if (siteWindow) {
4979 // This method call may cause mDocShell to become nullptr.
4980 siteWindow->Blur();
4982 // if the root is focused, clear the focus
4983 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
4984 if (fm && mDoc) {
4985 RefPtr<Element> element;
4986 fm->GetFocusedElementForWindow(this, false, nullptr,
4987 getter_AddRefs(element));
4988 if (element == mDoc->GetRootElement()) {
4989 fm->ClearFocus(this);
4995 void nsGlobalWindowOuter::StopOuter(ErrorResult& aError) {
4996 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
4997 if (webNav) {
4998 aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
5002 void nsGlobalWindowOuter::PrintOuter(ErrorResult& aError) {
5003 #ifdef NS_PRINTING
5004 if (!AreDialogsEnabled()) {
5005 // We probably want to keep throwing here; silently doing nothing is a bit
5006 // weird given the typical use cases of print().
5007 aError.Throw(NS_ERROR_NOT_AVAILABLE);
5008 return;
5011 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
5012 aError.Throw(NS_ERROR_NOT_AVAILABLE);
5013 return;
5016 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
5017 if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
5018 getter_AddRefs(webBrowserPrint)))) {
5019 nsAutoSyncOperation sync(GetCurrentInnerWindowInternal()
5020 ? GetCurrentInnerWindowInternal()->mDoc.get()
5021 : nullptr);
5023 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
5024 do_GetService("@mozilla.org/gfx/printsettings-service;1");
5026 nsCOMPtr<nsIPrintSettings> printSettings;
5027 if (printSettingsService) {
5028 bool printSettingsAreGlobal =
5029 Preferences::GetBool("print.use_global_printsettings", false);
5031 if (printSettingsAreGlobal) {
5032 printSettingsService->GetGlobalPrintSettings(
5033 getter_AddRefs(printSettings));
5035 nsAutoString printerName;
5036 printSettings->GetPrinterName(printerName);
5038 bool shouldGetDefaultPrinterName = printerName.IsEmpty();
5039 # ifdef MOZ_X11
5040 // In Linux, GTK backend does not support per printer settings.
5041 // Calling GetDefaultPrinterName causes a sandbox violation (see Bug
5042 // 1329216). The printer name is not needed anywhere else on Linux
5043 // before it gets to the parent. In the parent, we will then query the
5044 // default printer name if no name is set. Unless we are in the parent,
5045 // we will skip this part.
5046 if (!XRE_IsParentProcess()) {
5047 shouldGetDefaultPrinterName = false;
5049 # endif
5050 if (shouldGetDefaultPrinterName) {
5051 printSettingsService->GetDefaultPrinterName(printerName);
5052 printSettings->SetPrinterName(printerName);
5054 printSettingsService->InitPrintSettingsFromPrinter(printerName,
5055 printSettings);
5056 printSettingsService->InitPrintSettingsFromPrefs(
5057 printSettings, true, nsIPrintSettings::kInitSaveAll);
5058 } else {
5059 printSettingsService->GetNewPrintSettings(
5060 getter_AddRefs(printSettings));
5063 EnterModalState();
5064 webBrowserPrint->Print(printSettings, nullptr);
5065 LeaveModalState();
5067 bool savePrintSettings =
5068 Preferences::GetBool("print.save_print_settings", false);
5069 if (printSettingsAreGlobal && savePrintSettings) {
5070 printSettingsService->SavePrintSettingsToPrefs(
5071 printSettings, true, nsIPrintSettings::kInitSaveAll);
5072 printSettingsService->SavePrintSettingsToPrefs(
5073 printSettings, false, nsIPrintSettings::kInitSavePrinterName);
5075 } else {
5076 webBrowserPrint->GetGlobalPrintSettings(getter_AddRefs(printSettings));
5077 webBrowserPrint->Print(printSettings, nullptr);
5080 #endif // NS_PRINTING
5083 void nsGlobalWindowOuter::MoveToOuter(int32_t aXPos, int32_t aYPos,
5084 CallerType aCallerType,
5085 ErrorResult& aError) {
5087 * If caller is not chrome and the user has not explicitly exempted the site,
5088 * prevent window.moveTo() by exiting early
5091 if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
5092 return;
5095 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5096 if (!treeOwnerAsWin) {
5097 aError.Throw(NS_ERROR_FAILURE);
5098 return;
5101 nsCOMPtr<nsIScreenManager> screenMgr =
5102 do_GetService("@mozilla.org/gfx/screenmanager;1");
5103 nsCOMPtr<nsIScreen> screen;
5104 if (screenMgr) {
5105 CSSIntSize size;
5106 GetInnerSize(size);
5107 screenMgr->ScreenForRect(aXPos, aYPos, size.width, size.height,
5108 getter_AddRefs(screen));
5111 if (screen) {
5112 // On secondary displays, the "CSS px" coordinates are offset so that they
5113 // share their origin with global desktop pixels, to avoid ambiguities in
5114 // the coordinate space when there are displays with different DPIs.
5115 // (See the corresponding code in GetScreenXY() above.)
5116 int32_t screenLeftDeskPx, screenTopDeskPx, w, h;
5117 screen->GetRectDisplayPix(&screenLeftDeskPx, &screenTopDeskPx, &w, &h);
5118 CSSIntPoint cssPos(aXPos - screenLeftDeskPx, aYPos - screenTopDeskPx);
5119 CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
5121 double scale;
5122 screen->GetDefaultCSSScaleFactor(&scale);
5123 LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(scale);
5125 screen->GetContentsScaleFactor(&scale);
5126 DesktopPoint deskPos = devPos / DesktopToLayoutDeviceScale(scale);
5127 aError = treeOwnerAsWin->SetPositionDesktopPix(screenLeftDeskPx + deskPos.x,
5128 screenTopDeskPx + deskPos.y);
5129 } else {
5130 // We couldn't find a screen? Just assume a 1:1 mapping.
5131 CSSIntPoint cssPos(aXPos, aXPos);
5132 CheckSecurityLeftAndTop(&cssPos.x, &cssPos.y, aCallerType);
5133 LayoutDevicePoint devPos = cssPos * CSSToLayoutDeviceScale(1.0);
5134 aError = treeOwnerAsWin->SetPosition(devPos.x, devPos.y);
5137 CheckForDPIChange();
5140 void nsGlobalWindowOuter::MoveByOuter(int32_t aXDif, int32_t aYDif,
5141 CallerType aCallerType,
5142 ErrorResult& aError) {
5144 * If caller is not chrome and the user has not explicitly exempted the site,
5145 * prevent window.moveBy() by exiting early
5148 if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
5149 return;
5152 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5153 if (!treeOwnerAsWin) {
5154 aError.Throw(NS_ERROR_FAILURE);
5155 return;
5158 // To do this correctly we have to convert what we get from GetPosition
5159 // into CSS pixels, add the arguments, do the security check, and
5160 // then convert back to device pixels for the call to SetPosition.
5162 int32_t x, y;
5163 aError = treeOwnerAsWin->GetPosition(&x, &y);
5164 if (aError.Failed()) {
5165 return;
5168 // mild abuse of a "size" object so we don't need more helper functions
5169 nsIntSize cssPos(DevToCSSIntPixels(nsIntSize(x, y)));
5171 cssPos.width += aXDif;
5172 cssPos.height += aYDif;
5174 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerType);
5176 nsIntSize newDevPos(CSSToDevIntPixels(cssPos));
5178 aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height);
5180 CheckForDPIChange();
5183 nsresult nsGlobalWindowOuter::MoveBy(int32_t aXDif, int32_t aYDif) {
5184 ErrorResult rv;
5185 MoveByOuter(aXDif, aYDif, CallerType::System, rv);
5187 return rv.StealNSResult();
5190 void nsGlobalWindowOuter::ResizeToOuter(int32_t aWidth, int32_t aHeight,
5191 CallerType aCallerType,
5192 ErrorResult& aError) {
5194 * If caller is a browser-element then dispatch a resize event to
5195 * the embedder.
5197 if (mDocShell && mDocShell->GetIsMozBrowser()) {
5198 CSSIntSize size(aWidth, aHeight);
5199 if (!DispatchResizeEvent(size)) {
5200 // The embedder chose to prevent the default action for this
5201 // event, so let's not resize this window after all...
5202 return;
5207 * If caller is not chrome and the user has not explicitly exempted the site,
5208 * prevent window.resizeTo() by exiting early
5211 if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
5212 return;
5215 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5216 if (!treeOwnerAsWin) {
5217 aError.Throw(NS_ERROR_FAILURE);
5218 return;
5221 nsIntSize cssSize(aWidth, aHeight);
5222 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5224 nsIntSize devSz(CSSToDevIntPixels(cssSize));
5226 aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true);
5228 CheckForDPIChange();
5231 void nsGlobalWindowOuter::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif,
5232 CallerType aCallerType,
5233 ErrorResult& aError) {
5235 * If caller is a browser-element then dispatch a resize event to
5236 * parent.
5238 if (mDocShell && mDocShell->GetIsMozBrowser()) {
5239 CSSIntSize size;
5240 if (NS_FAILED(GetInnerSize(size))) {
5241 return;
5244 size.width += aWidthDif;
5245 size.height += aHeightDif;
5247 if (!DispatchResizeEvent(size)) {
5248 // The embedder chose to prevent the default action for this
5249 // event, so let's not resize this window after all...
5250 return;
5255 * If caller is not chrome and the user has not explicitly exempted the site,
5256 * prevent window.resizeBy() by exiting early
5259 if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
5260 return;
5263 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5264 if (!treeOwnerAsWin) {
5265 aError.Throw(NS_ERROR_FAILURE);
5266 return;
5269 int32_t width, height;
5270 aError = treeOwnerAsWin->GetSize(&width, &height);
5271 if (aError.Failed()) {
5272 return;
5275 // To do this correctly we have to convert what we got from GetSize
5276 // into CSS pixels, add the arguments, do the security check, and
5277 // then convert back to device pixels for the call to SetSize.
5279 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
5281 cssSize.width += aWidthDif;
5282 cssSize.height += aHeightDif;
5284 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5286 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
5288 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
5290 CheckForDPIChange();
5293 void nsGlobalWindowOuter::SizeToContentOuter(CallerType aCallerType,
5294 ErrorResult& aError) {
5295 if (!mDocShell) {
5296 return;
5300 * If caller is not chrome and the user has not explicitly exempted the site,
5301 * prevent window.sizeToContent() by exiting early
5304 if (!CanMoveResizeWindows(aCallerType) || IsFrame()) {
5305 return;
5308 // The content viewer does a check to make sure that it's a content
5309 // viewer for a toplevel docshell.
5310 nsCOMPtr<nsIContentViewer> cv;
5311 mDocShell->GetContentViewer(getter_AddRefs(cv));
5312 if (!cv) {
5313 aError.Throw(NS_ERROR_FAILURE);
5314 return;
5317 int32_t width, height;
5318 aError = cv->GetContentSize(&width, &height);
5319 if (aError.Failed()) {
5320 return;
5323 // Make sure the new size is following the CheckSecurityWidthAndHeight
5324 // rules.
5325 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
5326 if (!treeOwner) {
5327 aError.Throw(NS_ERROR_FAILURE);
5328 return;
5331 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
5332 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerType);
5334 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
5336 nsCOMPtr<nsIDocShell> docShell = mDocShell;
5337 aError =
5338 treeOwner->SizeShellTo(docShell, newDevSize.width, newDevSize.height);
5341 already_AddRefed<nsPIWindowRoot> nsGlobalWindowOuter::GetTopWindowRoot() {
5342 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
5343 if (!piWin) {
5344 return nullptr;
5347 nsCOMPtr<nsPIWindowRoot> window =
5348 do_QueryInterface(piWin->GetChromeEventHandler());
5349 return window.forget();
5352 void nsGlobalWindowOuter::FirePopupBlockedEvent(
5353 Document* aDoc, nsIURI* aPopupURI, const nsAString& aPopupWindowName,
5354 const nsAString& aPopupWindowFeatures) {
5355 MOZ_ASSERT(aDoc);
5357 // Fire a "DOMPopupBlocked" event so that the UI can hear about
5358 // blocked popups.
5359 PopupBlockedEventInit init;
5360 init.mBubbles = true;
5361 init.mCancelable = true;
5362 // XXX: This is a different object, but webidl requires an inner window here
5363 // now.
5364 init.mRequestingWindow = GetCurrentInnerWindowInternal();
5365 init.mPopupWindowURI = aPopupURI;
5366 init.mPopupWindowName = aPopupWindowName;
5367 init.mPopupWindowFeatures = aPopupWindowFeatures;
5369 RefPtr<PopupBlockedEvent> event = PopupBlockedEvent::Constructor(
5370 aDoc, NS_LITERAL_STRING("DOMPopupBlocked"), init);
5372 event->SetTrusted(true);
5374 aDoc->DispatchEvent(*event);
5377 void nsGlobalWindowOuter::NotifyContentBlockingEvent(
5378 unsigned aEvent, nsIChannel* aChannel, bool aBlocked, nsIURI* aURIHint,
5379 nsIChannel* aTrackingChannel,
5380 const mozilla::Maybe<AntiTrackingCommon::StorageAccessGrantedReason>&
5381 aReason) {
5382 MOZ_ASSERT(aURIHint);
5383 DebugOnly<bool> isCookiesBlockedTracker =
5384 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
5385 MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
5386 MOZ_ASSERT_IF(!isCookiesBlockedTracker, aReason.isNothing());
5387 MOZ_ASSERT_IF(isCookiesBlockedTracker && !aBlocked, aReason.isSome());
5389 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
5390 if (!docShell) {
5391 return;
5393 nsCOMPtr<Document> doc = docShell->GetDocument();
5394 NS_ENSURE_TRUE_VOID(doc);
5396 nsCOMPtr<nsIURI> uri(aURIHint);
5397 nsCOMPtr<nsIChannel> channel(aChannel);
5398 nsCOMPtr<nsIClassifiedChannel> trackingChannel =
5399 do_QueryInterface(aTrackingChannel);
5401 static bool prefInitialized = false;
5402 if (!prefInitialized) {
5403 Preferences::AddBoolVarCache(
5404 &gSyncContentBlockingNotifications,
5405 "dom.testing.sync-content-blocking-notifications", false);
5406 prefInitialized = true;
5409 nsCOMPtr<nsIRunnable> func = NS_NewRunnableFunction(
5410 "NotifyContentBlockingEventDelayed",
5411 [doc, docShell, uri, channel, aEvent, aBlocked, aReason,
5412 trackingChannel]() {
5413 // This event might come after the user has navigated to another
5414 // page. To prevent showing the TrackingProtection UI on the wrong
5415 // page, we need to check that the loading URI for the channel is
5416 // the same as the URI currently loaded in the document.
5417 if (!SameLoadingURI(doc, channel) &&
5418 aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
5419 return;
5422 // Notify nsIWebProgressListeners of this content blocking event.
5423 // Can be used to change the UI state.
5424 uint32_t event = 0;
5425 nsCOMPtr<nsISecureBrowserUI> securityUI;
5426 docShell->GetSecurityUI(getter_AddRefs(securityUI));
5427 if (!securityUI) {
5428 return;
5430 securityUI->GetContentBlockingEvent(&event);
5431 nsAutoCString origin;
5432 nsContentUtils::GetASCIIOrigin(uri, origin);
5434 bool blockedValue = aBlocked;
5435 bool unblocked = false;
5436 if (aEvent == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
5437 doc->SetHasTrackingContentBlocked(aBlocked, origin);
5438 if (!aBlocked) {
5439 unblocked = !doc->GetHasTrackingContentBlocked();
5441 } else if (aEvent ==
5442 nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT) {
5443 doc->SetHasTrackingContentLoaded(aBlocked, origin);
5444 if (!aBlocked) {
5445 unblocked = !doc->GetHasTrackingContentLoaded();
5447 } else if (aEvent == nsIWebProgressListener::
5448 STATE_BLOCKED_FINGERPRINTING_CONTENT) {
5449 doc->SetHasFingerprintingContentBlocked(aBlocked, origin);
5450 if (!aBlocked) {
5451 unblocked = !doc->GetHasFingerprintingContentBlocked();
5453 } else if (aEvent == nsIWebProgressListener::
5454 STATE_LOADED_FINGERPRINTING_CONTENT) {
5455 doc->SetHasFingerprintingContentLoaded(aBlocked, origin);
5456 if (!aBlocked) {
5457 unblocked = !doc->GetHasFingerprintingContentLoaded();
5459 } else if (aEvent ==
5460 nsIWebProgressListener::STATE_BLOCKED_CRYPTOMINING_CONTENT) {
5461 doc->SetHasCryptominingContentBlocked(aBlocked, origin);
5462 if (!aBlocked) {
5463 unblocked = !doc->GetHasCryptominingContentBlocked();
5465 } else if (aEvent ==
5466 nsIWebProgressListener::STATE_LOADED_CRYPTOMINING_CONTENT) {
5467 doc->SetHasCryptominingContentLoaded(aBlocked, origin);
5468 if (!aBlocked) {
5469 unblocked = !doc->GetHasCryptominingContentLoaded();
5471 } else if (aEvent == nsIWebProgressListener::
5472 STATE_COOKIES_BLOCKED_BY_PERMISSION) {
5473 doc->SetHasCookiesBlockedByPermission(aBlocked, origin);
5474 if (!aBlocked) {
5475 unblocked = !doc->GetHasCookiesBlockedByPermission();
5477 } else if (aEvent ==
5478 nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
5479 nsTArray<nsCString> trackingFullHashes;
5480 if (trackingChannel) {
5481 Unused << trackingChannel->GetMatchedTrackingFullHashes(
5482 trackingFullHashes);
5484 doc->SetHasTrackingCookiesBlocked(aBlocked, origin, aReason,
5485 trackingFullHashes);
5487 if (!aBlocked) {
5488 unblocked = !doc->GetHasTrackingCookiesBlocked();
5490 } else if (aEvent ==
5491 nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
5492 doc->SetHasAllCookiesBlocked(aBlocked, origin);
5493 if (!aBlocked) {
5494 unblocked = !doc->GetHasAllCookiesBlocked();
5496 } else if (aEvent ==
5497 nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
5498 doc->SetHasForeignCookiesBlocked(aBlocked, origin);
5499 if (!aBlocked) {
5500 unblocked = !doc->GetHasForeignCookiesBlocked();
5502 } else if (aEvent == nsIWebProgressListener::STATE_COOKIES_LOADED) {
5503 MOZ_ASSERT(!aBlocked,
5504 "We don't expected to see blocked STATE_COOKIES_LOADED");
5505 // Note that the logic in this branch is the logical negation of
5506 // the logic in other branches, since the Document API we have is
5507 // phrased in "loaded" terms as opposed to "blocked" terms.
5508 blockedValue = !aBlocked;
5509 doc->SetHasCookiesLoaded(blockedValue, origin);
5510 if (!aBlocked) {
5511 unblocked = !doc->GetHasCookiesLoaded();
5513 } else {
5514 // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
5516 const uint32_t oldEvent = event;
5517 if (blockedValue) {
5518 event |= aEvent;
5519 } else if (unblocked) {
5520 event &= ~aEvent;
5523 if (event == oldEvent
5524 #ifdef ANDROID
5525 // GeckoView always needs to notify about blocked trackers,
5526 // since the GeckoView API always needs to report the URI and
5527 // type of any blocked tracker. We use a platform-dependent code
5528 // path here because reporting this notification on desktop
5529 // platforms isn't necessary and doing so can have a big
5530 // performance cost.
5531 && aEvent != nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
5532 #endif
5534 // Avoid dispatching repeated notifications when nothing has
5535 // changed
5536 return;
5539 nsDocShell::Cast(docShell)->nsDocLoader::OnContentBlockingEvent(channel,
5540 event);
5542 nsresult rv;
5543 if (gSyncContentBlockingNotifications) {
5544 rv = func->Run();
5545 } else {
5546 rv = NS_DispatchToCurrentThreadQueue(func.forget(), 100,
5547 EventQueuePriority::Idle);
5549 if (NS_WARN_IF(NS_FAILED(rv))) {
5550 return;
5554 // static
5555 bool nsGlobalWindowOuter::SameLoadingURI(Document* aDoc, nsIChannel* aChannel) {
5556 nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
5557 if (!docURI) {
5558 return false;
5560 nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->LoadInfo();
5561 nsCOMPtr<nsIPrincipal> channelLoadingPrincipal =
5562 channelLoadInfo->LoadingPrincipal();
5563 if (!channelLoadingPrincipal) {
5564 // TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top
5565 // level loads should not be blocked by Tracking Protection, so we will
5566 // return false
5567 return false;
5569 nsCOMPtr<nsIURI> channelLoadingURI;
5570 channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
5571 if (!channelLoadingURI) {
5572 return false;
5574 bool equals = false;
5575 nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
5576 return NS_SUCCEEDED(rv) && equals;
5579 // static
5580 bool nsGlobalWindowOuter::CanSetProperty(const char* aPrefName) {
5581 // Chrome can set any property.
5582 if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
5583 return true;
5586 // If the pref is set to true, we can not set the property
5587 // and vice versa.
5588 return !Preferences::GetBool(aPrefName, true);
5591 bool nsGlobalWindowOuter::PopupWhitelisted() {
5592 if (mDoc && PopupBlocker::CanShowPopupByPermission(mDoc->NodePrincipal())) {
5593 return true;
5596 nsCOMPtr<nsPIDOMWindowOuter> parent = GetParent();
5597 if (parent == this) {
5598 return false;
5601 return nsGlobalWindowOuter::Cast(parent)->PopupWhitelisted();
5605 * Examine the current document state to see if we're in a way that is
5606 * typically abused by web designers. The window.open code uses this
5607 * routine to determine whether to allow the new window.
5608 * Returns a value from the PopupControlState enum.
5610 PopupBlocker::PopupControlState nsGlobalWindowOuter::RevisePopupAbuseLevel(
5611 PopupBlocker::PopupControlState aControl) {
5612 NS_ASSERTION(mDocShell, "Must have docshell");
5614 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
5615 return PopupBlocker::openAllowed;
5618 PopupBlocker::PopupControlState abuse = aControl;
5619 switch (abuse) {
5620 case PopupBlocker::openControlled:
5621 case PopupBlocker::openBlocked:
5622 case PopupBlocker::openOverridden:
5623 if (PopupWhitelisted())
5624 abuse = PopupBlocker::PopupControlState(abuse - 1);
5625 break;
5626 case PopupBlocker::openAbused:
5627 if (PopupWhitelisted())
5628 // Skip PopupBlocker::openBlocked
5629 abuse = PopupBlocker::openControlled;
5630 break;
5631 case PopupBlocker::openAllowed:
5632 break;
5633 default:
5634 NS_WARNING("Strange PopupControlState!");
5637 // limit the number of simultaneously open popups
5638 if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked ||
5639 abuse == PopupBlocker::openControlled) {
5640 int32_t popupMax = Preferences::GetInt("dom.popup_maximum", -1);
5641 if (popupMax >= 0 && gOpenPopupSpamCount >= popupMax)
5642 abuse = PopupBlocker::openOverridden;
5645 // If this popup is allowed, let's block any other for this event, forcing
5646 // PopupBlocker::openBlocked state.
5647 if ((abuse == PopupBlocker::openAllowed ||
5648 abuse == PopupBlocker::openControlled) &&
5649 StaticPrefs::dom_block_multiple_popups() && !PopupWhitelisted() &&
5650 !PopupBlocker::TryUsePopupOpeningToken(mDoc->NodePrincipal())) {
5651 abuse = PopupBlocker::openBlocked;
5654 return abuse;
5657 /* If a window open is blocked, fire the appropriate DOM events. */
5658 void nsGlobalWindowOuter::FireAbuseEvents(
5659 const nsAString& aPopupURL, const nsAString& aPopupWindowName,
5660 const nsAString& aPopupWindowFeatures) {
5661 // fetch the URI of the window requesting the opened window
5663 nsCOMPtr<nsPIDOMWindowOuter> window = GetTop();
5664 if (!window) {
5665 return;
5668 nsCOMPtr<Document> topDoc = window->GetDoc();
5669 nsCOMPtr<nsIURI> popupURI;
5671 // build the URI of the would-have-been popup window
5672 // (see nsWindowWatcher::URIfromURL)
5674 // first, fetch the opener's base URI
5676 nsIURI* baseURL = nullptr;
5678 nsCOMPtr<Document> doc = GetEntryDocument();
5679 if (doc) baseURL = doc->GetDocBaseURI();
5681 // use the base URI to build what would have been the popup's URI
5682 nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
5683 if (ios)
5684 ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), nullptr, baseURL,
5685 getter_AddRefs(popupURI));
5687 // fire an event block full of informative URIs
5688 FirePopupBlockedEvent(topDoc, popupURI, aPopupWindowName,
5689 aPopupWindowFeatures);
5692 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenOuter(
5693 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
5694 ErrorResult& aError) {
5695 nsCOMPtr<nsPIDOMWindowOuter> window;
5696 aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window));
5697 RefPtr<BrowsingContext> bc;
5698 if (!window || !(bc = window->GetBrowsingContext())) {
5699 return nullptr;
5701 return WindowProxyHolder(bc.forget());
5704 nsresult nsGlobalWindowOuter::Open(const nsAString& aUrl,
5705 const nsAString& aName,
5706 const nsAString& aOptions,
5707 nsDocShellLoadState* aLoadState,
5708 bool aForceNoOpener,
5709 nsPIDOMWindowOuter** _retval) {
5710 return OpenInternal(aUrl, aName, aOptions,
5711 false, // aDialog
5712 false, // aContentModal
5713 true, // aCalledNoScript
5714 false, // aDoJSFixups
5715 true, // aNavigate
5716 nullptr, nullptr, // No args
5717 aLoadState, aForceNoOpener, _retval);
5720 nsresult nsGlobalWindowOuter::OpenJS(const nsAString& aUrl,
5721 const nsAString& aName,
5722 const nsAString& aOptions,
5723 nsPIDOMWindowOuter** _retval) {
5724 return OpenInternal(aUrl, aName, aOptions,
5725 false, // aDialog
5726 false, // aContentModal
5727 false, // aCalledNoScript
5728 true, // aDoJSFixups
5729 true, // aNavigate
5730 nullptr, nullptr, // No args
5731 nullptr, // aLoadState
5732 false, // aForceNoOpener
5733 _retval);
5736 // like Open, but attaches to the new window any extra parameters past
5737 // [features] as a JS property named "arguments"
5738 nsresult nsGlobalWindowOuter::OpenDialog(const nsAString& aUrl,
5739 const nsAString& aName,
5740 const nsAString& aOptions,
5741 nsISupports* aExtraArgument,
5742 nsPIDOMWindowOuter** _retval) {
5743 return OpenInternal(aUrl, aName, aOptions,
5744 true, // aDialog
5745 false, // aContentModal
5746 true, // aCalledNoScript
5747 false, // aDoJSFixups
5748 true, // aNavigate
5749 nullptr, aExtraArgument, // Arguments
5750 nullptr, // aLoadState
5751 false, // aForceNoOpener
5752 _retval);
5755 // Like Open, but passes aNavigate=false.
5756 /* virtual */
5757 nsresult nsGlobalWindowOuter::OpenNoNavigate(const nsAString& aUrl,
5758 const nsAString& aName,
5759 const nsAString& aOptions,
5760 nsPIDOMWindowOuter** _retval) {
5761 return OpenInternal(aUrl, aName, aOptions,
5762 false, // aDialog
5763 false, // aContentModal
5764 true, // aCalledNoScript
5765 false, // aDoJSFixups
5766 false, // aNavigate
5767 nullptr, nullptr, // No args
5768 nullptr, // aLoadState
5769 false, // aForceNoOpener
5770 _retval);
5773 Nullable<WindowProxyHolder> nsGlobalWindowOuter::OpenDialogOuter(
5774 JSContext* aCx, const nsAString& aUrl, const nsAString& aName,
5775 const nsAString& aOptions, const Sequence<JS::Value>& aExtraArgument,
5776 ErrorResult& aError) {
5777 nsCOMPtr<nsIJSArgArray> argvArray;
5778 aError =
5779 NS_CreateJSArgv(aCx, aExtraArgument.Length(), aExtraArgument.Elements(),
5780 getter_AddRefs(argvArray));
5781 if (aError.Failed()) {
5782 return nullptr;
5785 nsCOMPtr<nsPIDOMWindowOuter> dialog;
5786 aError = OpenInternal(aUrl, aName, aOptions,
5787 true, // aDialog
5788 false, // aContentModal
5789 false, // aCalledNoScript
5790 false, // aDoJSFixups
5791 true, // aNavigate
5792 argvArray, nullptr, // Arguments
5793 nullptr, // aLoadState
5794 false, // aForceNoOpener
5795 getter_AddRefs(dialog));
5796 RefPtr<BrowsingContext> bc;
5797 if (!dialog || !(bc = dialog->GetBrowsingContext())) {
5798 return nullptr;
5800 return WindowProxyHolder(bc.forget());
5803 BrowsingContext* nsGlobalWindowOuter::GetFramesOuter() {
5804 RefPtr<nsPIDOMWindowOuter> frames(this);
5805 FlushPendingNotifications(FlushType::ContentAndNotify);
5806 return mBrowsingContext;
5809 /* static */
5810 nsGlobalWindowInner* nsGlobalWindowOuter::CallerInnerWindow(JSContext* aCx) {
5811 nsIGlobalObject* global = GetIncumbentGlobal();
5812 NS_ENSURE_TRUE(global, nullptr);
5813 JS::Rooted<JSObject*> scope(aCx, global->GetGlobalJSObject());
5814 NS_ENSURE_TRUE(scope, nullptr);
5816 // When Jetpack runs content scripts inside a sandbox, it uses
5817 // sandboxPrototype to make them appear as though they're running in the
5818 // scope of the page. So when a content script invokes postMessage, it expects
5819 // the |source| of the received message to be the window set as the
5820 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
5821 // now we need to do some special handling to support it.
5822 if (xpc::IsSandbox(scope)) {
5823 JSAutoRealm ar(aCx, scope);
5824 JS::Rooted<JSObject*> scopeProto(aCx);
5825 bool ok = JS_GetPrototype(aCx, scope, &scopeProto);
5826 NS_ENSURE_TRUE(ok, nullptr);
5827 if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) &&
5828 // Our current Realm on aCx is the sandbox. Using that for the
5829 // CheckedUnwrapDynamic call makes sense: if the sandbox can unwrap the
5830 // window, we can use it. And we do want CheckedUnwrapDynamic, because
5831 // the whole point is to unwrap windows.
5832 (scopeProto = js::CheckedUnwrapDynamic(
5833 scopeProto, aCx, /* stopAtWindowProxy = */ false))) {
5834 global = xpc::NativeGlobal(scopeProto);
5835 NS_ENSURE_TRUE(global, nullptr);
5839 // The calling window must be holding a reference, so we can return a weak
5840 // pointer.
5841 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
5842 return nsGlobalWindowInner::Cast(win);
5845 /* static */
5846 bool nsGlobalWindowOuter::GatherPostMessageData(
5847 JSContext* aCx, const nsAString& aTargetOrigin, BrowsingContext** aSource,
5848 nsAString& aOrigin, nsIURI** aTargetOriginURI,
5849 nsIPrincipal** aCallerPrincipal, nsGlobalWindowInner** aCallerInnerWindow,
5850 nsIURI** aCallerDocumentURI, ErrorResult& aError) {
5852 // Window.postMessage is an intentional subversion of the same-origin policy.
5853 // As such, this code must be particularly careful in the information it
5854 // exposes to calling code.
5856 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5859 // First, get the caller's window
5860 RefPtr<nsGlobalWindowInner> callerInnerWin = CallerInnerWindow(aCx);
5861 nsIPrincipal* callerPrin;
5862 if (callerInnerWin) {
5863 RefPtr<Document> doc = callerInnerWin->GetExtantDoc();
5864 if (!doc) {
5865 return false;
5867 NS_IF_ADDREF(*aCallerDocumentURI = doc->GetDocumentURI());
5869 // Compute the caller's origin either from its principal or, in the case the
5870 // principal doesn't carry a URI (e.g. the system principal), the caller's
5871 // document. We must get this now instead of when the event is created and
5872 // dispatched, because ultimately it is the identity of the calling window
5873 // *now* that determines who sent the message (and not an identity which
5874 // might have changed due to intervening navigations).
5875 callerPrin = callerInnerWin->GetPrincipal();
5876 } else {
5877 // In case the global is not a window, it can be a sandbox, and the
5878 // sandbox's principal can be used for the security check.
5879 nsIGlobalObject* global = GetIncumbentGlobal();
5880 NS_ASSERTION(global, "Why is there no global object?");
5881 callerPrin = global->PrincipalOrNull();
5883 if (!callerPrin) {
5884 return false;
5887 nsCOMPtr<nsIURI> callerOuterURI;
5888 if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) {
5889 return false;
5892 if (callerOuterURI) {
5893 // if the principal has a URI, use that to generate the origin
5894 nsContentUtils::GetUTFOrigin(callerPrin, aOrigin);
5895 } else if (callerInnerWin) {
5896 if (!*aCallerDocumentURI) {
5897 return false;
5899 // otherwise use the URI of the document to generate origin
5900 nsContentUtils::GetUTFOrigin(*aCallerDocumentURI, aOrigin);
5901 } else {
5902 // in case of a sandbox with a system principal origin can be empty
5903 if (!nsContentUtils::IsSystemPrincipal(callerPrin)) {
5904 return false;
5907 NS_IF_ADDREF(*aCallerPrincipal = callerPrin);
5909 // "/" indicates same origin as caller, "*" indicates no specific origin is
5910 // required.
5911 if (!aTargetOrigin.EqualsASCII("/") && !aTargetOrigin.EqualsASCII("*")) {
5912 nsCOMPtr<nsIURI> targetOriginURI;
5913 if (NS_FAILED(NS_NewURI(getter_AddRefs(targetOriginURI), aTargetOrigin))) {
5914 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5915 return false;
5918 nsresult rv = NS_MutateURI(targetOriginURI)
5919 .SetUserPass(EmptyCString())
5920 .SetPathQueryRef(EmptyCString())
5921 .Finalize(aTargetOriginURI);
5922 if (NS_FAILED(rv)) {
5923 return false;
5927 if (!nsContentUtils::IsCallerChrome() && callerInnerWin &&
5928 callerInnerWin->GetOuterWindowInternal()) {
5929 NS_ADDREF(*aSource = callerInnerWin->GetOuterWindowInternal()
5930 ->GetBrowsingContext());
5931 } else {
5932 *aSource = nullptr;
5935 callerInnerWin.forget(aCallerInnerWindow);
5937 return true;
5940 bool nsGlobalWindowOuter::GetPrincipalForPostMessage(
5941 const nsAString& aTargetOrigin, nsIURI* aTargetOriginURI,
5942 nsIPrincipal* aCallerPrincipal, nsIPrincipal& aSubjectPrincipal,
5943 nsIPrincipal** aProvidedPrincipal) {
5945 // Window.postMessage is an intentional subversion of the same-origin policy.
5946 // As such, this code must be particularly careful in the information it
5947 // exposes to calling code.
5949 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
5952 // Convert the provided origin string into a URI for comparison purposes.
5953 nsCOMPtr<nsIPrincipal> providedPrincipal;
5955 if (aTargetOrigin.EqualsASCII("/")) {
5956 providedPrincipal = aCallerPrincipal;
5958 // "*" indicates no specific origin is required.
5959 else if (!aTargetOrigin.EqualsASCII("*")) {
5960 OriginAttributes attrs = aSubjectPrincipal.OriginAttributesRef();
5961 if (aSubjectPrincipal.IsSystemPrincipal()) {
5962 auto principal = BasePrincipal::Cast(GetPrincipal());
5964 if (attrs != principal->OriginAttributesRef()) {
5965 nsCOMPtr<nsIURI> targetURI;
5966 nsAutoCString targetURL;
5967 nsAutoCString sourceOrigin;
5968 nsAutoCString targetOrigin;
5970 if (NS_FAILED(principal->GetURI(getter_AddRefs(targetURI))) ||
5971 NS_FAILED(targetURI->GetAsciiSpec(targetURL)) ||
5972 NS_FAILED(principal->GetOrigin(targetOrigin)) ||
5973 NS_FAILED(aSubjectPrincipal.GetOrigin(sourceOrigin))) {
5974 NS_WARNING("Failed to get source and target origins");
5975 return false;
5978 nsContentUtils::LogSimpleConsoleError(
5979 NS_ConvertUTF8toUTF16(nsPrintfCString(
5980 R"(Attempting to post a message to window with url "%s" and )"
5981 R"(origin "%s" from a system principal scope with mismatched )"
5982 R"(origin "%s".)",
5983 targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
5984 "DOM", !!principal->PrivateBrowsingId(),
5985 nsContentUtils::IsSystemPrincipal(principal));
5987 attrs = principal->OriginAttributesRef();
5991 // Create a nsIPrincipal inheriting the app/browser attributes from the
5992 // caller.
5993 providedPrincipal =
5994 BasePrincipal::CreateCodebasePrincipal(aTargetOriginURI, attrs);
5995 if (NS_WARN_IF(!providedPrincipal)) {
5996 return false;
5998 } else {
5999 // We still need to check the originAttributes if the target origin is '*'.
6000 // But we will ingore the FPD here since the FPDs are possible to be
6001 // different.
6002 auto principal = BasePrincipal::Cast(GetPrincipal());
6003 NS_ENSURE_TRUE(principal, false);
6005 OriginAttributes targetAttrs = principal->OriginAttributesRef();
6006 OriginAttributes sourceAttrs = aSubjectPrincipal.OriginAttributesRef();
6007 // We have to exempt the check of OA if the subject prioncipal is a system
6008 // principal since there are many tests try to post messages to content from
6009 // chrome with a mismatch OA. For example, using the ContentTask.spawn() to
6010 // post a message into a private browsing window. The injected code in
6011 // ContentTask.spawn() will be executed under the system principal and the
6012 // OA of the system principal mismatches with the OA of a private browsing
6013 // window.
6014 MOZ_DIAGNOSTIC_ASSERT(aSubjectPrincipal.IsSystemPrincipal() ||
6015 sourceAttrs.EqualsIgnoringFPD(targetAttrs));
6017 // If 'privacy.firstparty.isolate.block_post_message' is true, we will block
6018 // postMessage across different first party domains.
6019 if (OriginAttributes::IsBlockPostMessageForFPI() &&
6020 !aSubjectPrincipal.IsSystemPrincipal() &&
6021 sourceAttrs.mFirstPartyDomain != targetAttrs.mFirstPartyDomain) {
6022 return false;
6026 providedPrincipal.forget(aProvidedPrincipal);
6027 return true;
6030 void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
6031 JS::Handle<JS::Value> aMessage,
6032 const nsAString& aTargetOrigin,
6033 JS::Handle<JS::Value> aTransfer,
6034 nsIPrincipal& aSubjectPrincipal,
6035 ErrorResult& aError) {
6036 RefPtr<BrowsingContext> sourceBc;
6037 nsAutoString origin;
6038 nsCOMPtr<nsIURI> targetOriginURI;
6039 nsCOMPtr<nsIPrincipal> callerPrincipal;
6040 RefPtr<nsGlobalWindowInner> callerInnerWindow;
6041 nsCOMPtr<nsIURI> callerDocumentURI;
6042 if (!GatherPostMessageData(aCx, aTargetOrigin, getter_AddRefs(sourceBc),
6043 origin, getter_AddRefs(targetOriginURI),
6044 getter_AddRefs(callerPrincipal),
6045 getter_AddRefs(callerInnerWindow),
6046 getter_AddRefs(callerDocumentURI), aError)) {
6047 return;
6050 nsCOMPtr<nsIPrincipal> providedPrincipal;
6051 if (!GetPrincipalForPostMessage(aTargetOrigin, targetOriginURI,
6052 callerPrincipal, aSubjectPrincipal,
6053 getter_AddRefs(providedPrincipal))) {
6054 return;
6057 // Create and asynchronously dispatch a runnable which will handle actual DOM
6058 // event creation and dispatch.
6059 RefPtr<PostMessageEvent> event = new PostMessageEvent(
6060 sourceBc, origin, this, providedPrincipal,
6061 callerInnerWindow ? callerInnerWindow->WindowID() : 0, callerDocumentURI);
6063 event->Write(aCx, aMessage, aTransfer, aError);
6064 if (NS_WARN_IF(aError.Failed())) {
6065 return;
6068 if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
6069 if (mDoc) {
6070 Document* doc = mDoc->GetTopLevelContentDocument();
6071 if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
6072 // As long as the top level is loading, we can dispatch events to the
6073 // queue because the queue will be flushed eventually
6074 mozilla::dom::TabGroup* tabGroup = TabGroup();
6075 aError = tabGroup->QueuePostMessageEvent(event.forget());
6076 return;
6081 aError = Dispatch(TaskCategory::Other, event.forget());
6084 class nsCloseEvent : public Runnable {
6085 RefPtr<nsGlobalWindowOuter> mWindow;
6086 bool mIndirect;
6088 nsCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect)
6089 : mozilla::Runnable("nsCloseEvent"),
6090 mWindow(aWindow),
6091 mIndirect(aIndirect) {}
6093 public:
6094 static nsresult PostCloseEvent(nsGlobalWindowOuter* aWindow, bool aIndirect) {
6095 nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
6096 nsresult rv = aWindow->Dispatch(TaskCategory::Other, ev.forget());
6097 if (NS_SUCCEEDED(rv)) aWindow->MaybeForgiveSpamCount();
6098 return rv;
6101 NS_IMETHOD Run() override {
6102 if (mWindow) {
6103 if (mIndirect) {
6104 return PostCloseEvent(mWindow, false);
6106 mWindow->ReallyCloseWindow();
6108 return NS_OK;
6112 bool nsGlobalWindowOuter::CanClose() {
6113 if (mIsChrome) {
6114 nsCOMPtr<nsIBrowserDOMWindow> bwin;
6115 GetBrowserDOMWindow(getter_AddRefs(bwin));
6117 bool canClose = true;
6118 if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
6119 return canClose;
6123 if (!mDocShell) {
6124 return true;
6127 // Ask the content viewer whether the toplevel window can close.
6128 // If the content viewer returns false, it is responsible for calling
6129 // Close() as soon as it is possible for the window to close.
6130 // This allows us to not close the window while printing is happening.
6132 nsCOMPtr<nsIContentViewer> cv;
6133 mDocShell->GetContentViewer(getter_AddRefs(cv));
6134 if (cv) {
6135 bool canClose;
6136 nsresult rv = cv->PermitUnload(&canClose);
6137 if (NS_SUCCEEDED(rv) && !canClose) return false;
6139 rv = cv->RequestWindowClose(&canClose);
6140 if (NS_SUCCEEDED(rv) && !canClose) return false;
6143 return true;
6146 void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) {
6147 if (!mDocShell || IsInModalState() ||
6148 (IsFrame() && !mDocShell->GetIsMozBrowser())) {
6149 // window.close() is called on a frame in a frameset, on a window
6150 // that's already closed, or on a window for which there's
6151 // currently a modal dialog open. Ignore such calls.
6152 return;
6155 if (mHavePendingClose) {
6156 // We're going to be closed anyway; do nothing since we don't want
6157 // to double-close
6158 return;
6161 if (mBlockScriptedClosingFlag) {
6162 // A script's popup has been blocked and we don't want
6163 // the window to be closed directly after this event,
6164 // so the user can see that there was a blocked popup.
6165 return;
6168 // Don't allow scripts from content to close non-neterror windows that
6169 // were not opened by script.
6170 if (mDoc) {
6171 nsAutoString url;
6172 nsresult rv = mDoc->GetURL(url);
6173 NS_ENSURE_SUCCESS_VOID(rv);
6175 if (!StringBeginsWith(url, NS_LITERAL_STRING("about:neterror")) &&
6176 !mHadOriginalOpener && !aTrustedCaller) {
6177 bool allowClose =
6178 mAllowScriptsToClose ||
6179 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
6180 if (!allowClose) {
6181 // We're blocking the close operation
6182 // report localized error msg in JS console
6183 nsContentUtils::ReportToConsole(
6184 nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM Window"),
6185 mDoc, // Better name for the category?
6186 nsContentUtils::eDOM_PROPERTIES, "WindowCloseBlockedWarning");
6188 return;
6193 if (!mInClose && !mIsClosed && !CanClose()) {
6194 return;
6197 // Fire a DOM event notifying listeners that this window is about to
6198 // be closed. The tab UI code may choose to cancel the default
6199 // action for this event, if so, we won't actually close the window
6200 // (since the tab UI code will close the tab in stead). Sure, this
6201 // could be abused by content code, but do we care? I don't think
6202 // so...
6204 bool wasInClose = mInClose;
6205 mInClose = true;
6207 if (!DispatchCustomEvent(NS_LITERAL_STRING("DOMWindowClose"))) {
6208 // Someone chose to prevent the default action for this event, if
6209 // so, let's not close this window after all...
6211 mInClose = wasInClose;
6212 return;
6215 FinalClose();
6218 nsresult nsGlobalWindowOuter::Close() {
6219 CloseOuter(/* aTrustedCaller = */ true);
6220 return NS_OK;
6223 void nsGlobalWindowOuter::ForceClose() {
6224 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
6226 if (IsFrame() || !mDocShell) {
6227 // This may be a frame in a frameset, or a window that's already closed.
6228 // Ignore such calls.
6229 return;
6232 if (mHavePendingClose) {
6233 // We're going to be closed anyway; do nothing since we don't want
6234 // to double-close
6235 return;
6238 mInClose = true;
6240 DispatchCustomEvent(NS_LITERAL_STRING("DOMWindowClose"));
6242 FinalClose();
6245 void nsGlobalWindowOuter::FinalClose() {
6246 // Flag that we were closed.
6247 mIsClosed = true;
6249 // If we get here from CloseOuter then it means that the parent process is
6250 // going to close our window for us. It's just important to set mIsClosed.
6251 if (XRE_GetProcessType() == GeckoProcessType_Content) {
6252 return;
6255 // This stuff is non-sensical but incredibly fragile. The reasons for the
6256 // behavior here don't make sense today and may not have ever made sense,
6257 // but various bits of frontend code break when you change them. If you need
6258 // to fix up this behavior, feel free to. It's a righteous task, but involves
6259 // wrestling with various download manager tests, frontend code, and possible
6260 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
6261 // testing ground.
6263 // In particular, if some inner of |win| is the entry global, we must
6264 // complete _two_ round-trips to the event loop before the call to
6265 // ReallyCloseWindow. This allows setTimeout handlers that are set after
6266 // FinalClose() is called to run before the window is torn down.
6267 nsCOMPtr<nsPIDOMWindowInner> entryWindow =
6268 do_QueryInterface(GetEntryGlobal());
6269 bool indirect = entryWindow && entryWindow->GetOuterWindow() == this;
6270 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
6271 ReallyCloseWindow();
6272 } else {
6273 mHavePendingClose = true;
6277 void nsGlobalWindowOuter::ReallyCloseWindow() {
6278 // Make sure we never reenter this method.
6279 mHavePendingClose = true;
6281 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6283 // If there's no treeOwnerAsWin, this window must already be closed.
6285 if (treeOwnerAsWin) {
6286 // but if we're a browser window we could be in some nasty
6287 // self-destroying cascade that we should mostly ignore
6289 if (mDocShell) {
6290 nsCOMPtr<nsIBrowserDOMWindow> bwin;
6291 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6292 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6293 nsCOMPtr<nsPIDOMWindowOuter> rootWin =
6294 rootItem ? rootItem->GetWindow() : nullptr;
6295 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
6296 if (chromeWin) chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
6298 if (rootWin) {
6299 /* Normally we destroy the entire window, but not if
6300 this DOM window belongs to a tabbed browser and doesn't
6301 correspond to a tab. This allows a well-behaved tab
6302 to destroy the container as it should but is a final measure
6303 to prevent an errant tab from doing so when it shouldn't.
6304 This works because we reach this code when we shouldn't only
6305 in the particular circumstance that we belong to a tab
6306 that has just been closed (and is therefore already missing
6307 from the list of browsers) (and has an unload handler
6308 that closes the window). */
6309 // XXXbz now that we have mHavePendingClose, is this needed?
6310 bool isTab;
6311 if (rootWin == this || !bwin ||
6312 (NS_SUCCEEDED(bwin->IsTabContentWindow(this, &isTab)) && isTab)) {
6313 treeOwnerAsWin->Destroy();
6318 CleanUp();
6322 void nsGlobalWindowOuter::EnterModalState() {
6323 // GetScriptableTop, not GetTop, so that EnterModalState works properly with
6324 // <iframe mozbrowser>.
6325 nsGlobalWindowOuter* topWin = GetScriptableTopInternal();
6327 if (!topWin) {
6328 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
6329 return;
6332 // If there is an active ESM in this window, clear it. Otherwise, this can
6333 // cause a problem if a modal state is entered during a mouseup event.
6334 EventStateManager* activeESM = static_cast<EventStateManager*>(
6335 EventStateManager::GetActiveEventStateManager());
6336 if (activeESM && activeESM->GetPresContext()) {
6337 PresShell* activePresShell = activeESM->GetPresContext()->GetPresShell();
6338 if (activePresShell && (nsContentUtils::ContentIsCrossDocDescendantOf(
6339 activePresShell->GetDocument(), mDoc) ||
6340 nsContentUtils::ContentIsCrossDocDescendantOf(
6341 mDoc, activePresShell->GetDocument()))) {
6342 EventStateManager::ClearGlobalActiveContent(activeESM);
6344 PresShell::ReleaseCapturingContent();
6346 if (activePresShell) {
6347 RefPtr<nsFrameSelection> frameSelection =
6348 activePresShell->FrameSelection();
6349 frameSelection->SetDragState(false);
6354 // If there are any drag and drop operations in flight, try to end them.
6355 nsCOMPtr<nsIDragService> ds =
6356 do_GetService("@mozilla.org/widget/dragservice;1");
6357 if (ds) {
6358 ds->EndDragSession(true, 0);
6361 // Clear the capturing content if it is under topDoc.
6362 // Usually the activeESM check above does that, but there are cases when
6363 // we don't have activeESM, or it is for different document.
6364 Document* topDoc = topWin->GetExtantDoc();
6365 nsIContent* capturingContent = PresShell::GetCapturingContent();
6366 if (capturingContent && topDoc &&
6367 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
6368 PresShell::ReleaseCapturingContent();
6371 if (topWin->mModalStateDepth == 0) {
6372 NS_ASSERTION(!topWin->mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
6374 topWin->mSuspendedDoc = topDoc;
6375 if (topDoc) {
6376 topDoc->SuppressEventHandling();
6379 nsGlobalWindowInner* inner = topWin->GetCurrentInnerWindowInternal();
6380 if (inner) {
6381 topWin->GetCurrentInnerWindowInternal()->Suspend();
6384 topWin->mModalStateDepth++;
6387 void nsGlobalWindowOuter::LeaveModalState() {
6388 nsGlobalWindowOuter* topWin = GetScriptableTopInternal();
6390 if (!topWin) {
6391 NS_ERROR("Uh, LeaveModalState() called w/o a reachable top window?");
6392 return;
6395 MOZ_ASSERT(topWin->mModalStateDepth != 0);
6396 MOZ_ASSERT(IsSuspended());
6397 MOZ_ASSERT(topWin->IsSuspended());
6398 topWin->mModalStateDepth--;
6400 nsGlobalWindowInner* inner = topWin->GetCurrentInnerWindowInternal();
6402 if (topWin->mModalStateDepth == 0) {
6403 if (inner) {
6404 inner->Resume();
6407 if (topWin->mSuspendedDoc) {
6408 nsCOMPtr<Document> currentDoc = topWin->GetExtantDoc();
6409 topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(
6410 currentDoc == topWin->mSuspendedDoc);
6411 topWin->mSuspendedDoc = nullptr;
6415 // Remember the time of the last dialog quit.
6416 if (inner) {
6417 inner->mLastDialogQuitTime = TimeStamp::Now();
6420 if (topWin->mModalStateDepth == 0) {
6421 RefPtr<Event> event = NS_NewDOMEvent(inner, nullptr, nullptr);
6422 event->InitEvent(NS_LITERAL_STRING("endmodalstate"), true, false);
6423 event->SetTrusted(true);
6424 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
6425 topWin->DispatchEvent(*event);
6429 bool nsGlobalWindowOuter::IsInModalState() {
6430 nsGlobalWindowOuter* topWin = GetScriptableTopInternal();
6432 if (!topWin) {
6433 // IsInModalState() getting called w/o a reachable top window is a bit
6434 // iffy, but valid enough not to make noise about it. See bug 404828
6435 return false;
6438 return topWin->mModalStateDepth != 0;
6441 void nsGlobalWindowOuter::NotifyWindowIDDestroyed(const char* aTopic) {
6442 nsCOMPtr<nsIRunnable> runnable =
6443 new WindowDestroyedEvent(this, mWindowID, aTopic);
6444 Dispatch(TaskCategory::Other, runnable.forget());
6447 Element* nsGlobalWindowOuter::GetFrameElementOuter(
6448 nsIPrincipal& aSubjectPrincipal) {
6449 if (!mDocShell || mDocShell->GetIsMozBrowser()) {
6450 return nullptr;
6453 // Per HTML5, the frameElement getter returns null in cross-origin situations.
6454 Element* element = GetRealFrameElementOuter();
6455 if (!element) {
6456 return nullptr;
6459 if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) {
6460 return nullptr;
6463 return element;
6466 Element* nsGlobalWindowOuter::GetRealFrameElementOuter() {
6467 if (!mDocShell) {
6468 return nullptr;
6471 nsCOMPtr<nsIDocShell> parent;
6472 mDocShell->GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent));
6474 if (!parent || parent == mDocShell) {
6475 // We're at a chrome boundary, don't expose the chrome iframe
6476 // element to content code.
6477 return nullptr;
6480 return mFrameElement;
6484 * nsIGlobalWindow::GetFrameElement (when called from C++) is just a wrapper
6485 * around GetRealFrameElement.
6487 Element* nsGlobalWindowOuter::GetFrameElement() {
6488 FORWARD_TO_INNER(GetFrameElement, (), nullptr);
6491 namespace {
6492 class ChildCommandDispatcher : public Runnable {
6493 public:
6494 ChildCommandDispatcher(nsPIWindowRoot* aRoot, nsIBrowserChild* aBrowserChild,
6495 const nsAString& aAction)
6496 : mozilla::Runnable("ChildCommandDispatcher"),
6497 mRoot(aRoot),
6498 mBrowserChild(aBrowserChild),
6499 mAction(aAction) {}
6501 NS_IMETHOD Run() override {
6502 nsTArray<nsCString> enabledCommands, disabledCommands;
6503 mRoot->GetEnabledDisabledCommands(enabledCommands, disabledCommands);
6504 if (enabledCommands.Length() || disabledCommands.Length()) {
6505 mBrowserChild->EnableDisableCommands(mAction, enabledCommands,
6506 disabledCommands);
6509 return NS_OK;
6512 private:
6513 nsCOMPtr<nsPIWindowRoot> mRoot;
6514 nsCOMPtr<nsIBrowserChild> mBrowserChild;
6515 nsString mAction;
6518 class CommandDispatcher : public Runnable {
6519 public:
6520 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
6521 const nsAString& aAction)
6522 : mozilla::Runnable("CommandDispatcher"),
6523 mDispatcher(aDispatcher),
6524 mAction(aAction) {}
6526 NS_IMETHOD Run() override { return mDispatcher->UpdateCommands(mAction); }
6528 nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
6529 nsString mAction;
6531 } // anonymous namespace
6533 void nsGlobalWindowOuter::UpdateCommands(const nsAString& anAction,
6534 Selection* aSel, int16_t aReason) {
6535 // If this is a child process, redirect to the parent process.
6536 if (nsIDocShell* docShell = GetDocShell()) {
6537 if (nsCOMPtr<nsIBrowserChild> child = docShell->GetBrowserChild()) {
6538 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
6539 if (root) {
6540 nsContentUtils::AddScriptRunner(
6541 new ChildCommandDispatcher(root, child, anAction));
6543 return;
6547 nsPIDOMWindowOuter* rootWindow = GetPrivateRoot();
6548 if (!rootWindow) {
6549 return;
6552 Document* doc = rootWindow->GetExtantDoc();
6554 if (!doc) {
6555 return;
6557 // selectionchange action is only used for mozbrowser, not for XUL. So we
6558 // bypass XUL command dispatch if anAction is "selectionchange".
6559 if (!anAction.EqualsLiteral("selectionchange")) {
6560 // Retrieve the command dispatcher and call updateCommands on it.
6561 nsIDOMXULCommandDispatcher* xulCommandDispatcher =
6562 doc->GetCommandDispatcher();
6563 if (xulCommandDispatcher) {
6564 nsContentUtils::AddScriptRunner(
6565 new CommandDispatcher(xulCommandDispatcher, anAction));
6570 Selection* nsGlobalWindowOuter::GetSelectionOuter() {
6571 if (!mDocShell) {
6572 return nullptr;
6575 PresShell* presShell = mDocShell->GetPresShell();
6576 if (!presShell) {
6577 return nullptr;
6579 return presShell->GetCurrentSelection(SelectionType::eNormal);
6582 already_AddRefed<Selection> nsGlobalWindowOuter::GetSelection() {
6583 RefPtr<Selection> selection = GetSelectionOuter();
6584 return selection.forget();
6587 bool nsGlobalWindowOuter::FindOuter(const nsAString& aString,
6588 bool aCaseSensitive, bool aBackwards,
6589 bool aWrapAround, bool aWholeWord,
6590 bool aSearchInFrames, bool aShowDialog,
6591 ErrorResult& aError) {
6592 Unused << aShowDialog;
6594 if (Preferences::GetBool("dom.disable_window_find", false)) {
6595 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6596 return false;
6599 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
6600 if (!finder) {
6601 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6602 return false;
6605 // Set the options of the search
6606 aError = finder->SetSearchString(aString);
6607 if (aError.Failed()) {
6608 return false;
6610 finder->SetMatchCase(aCaseSensitive);
6611 finder->SetFindBackwards(aBackwards);
6612 finder->SetWrapFind(aWrapAround);
6613 finder->SetEntireWord(aWholeWord);
6614 finder->SetSearchFrames(aSearchInFrames);
6616 // the nsIWebBrowserFind is initialized to use this window
6617 // as the search root, but uses focus to set the current search
6618 // frame. If we're being called from JS (as here), this window
6619 // should be the current search frame.
6620 nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
6621 if (framesFinder) {
6622 framesFinder->SetRootSearchFrame(this); // paranoia
6623 framesFinder->SetCurrentSearchFrame(this);
6626 if (aString.IsEmpty()) {
6627 return false;
6630 // Launch the search with the passed in search string
6631 bool didFind = false;
6632 aError = finder->FindNext(&didFind);
6633 return didFind;
6636 //*****************************************************************************
6637 // EventTarget
6638 //*****************************************************************************
6640 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetOwnerGlobalForBindingsInternal() {
6641 return this;
6644 bool nsGlobalWindowOuter::DispatchEvent(Event& aEvent, CallerType aCallerType,
6645 ErrorResult& aRv) {
6646 FORWARD_TO_INNER(DispatchEvent, (aEvent, aCallerType, aRv), false);
6649 bool nsGlobalWindowOuter::ComputeDefaultWantsUntrusted(ErrorResult& aRv) {
6650 // It's OK that we just return false here on failure to create an
6651 // inner. GetOrCreateListenerManager() will likewise fail, and then
6652 // we won't be adding any listeners anyway.
6653 FORWARD_TO_INNER_CREATE(ComputeDefaultWantsUntrusted, (aRv), false);
6656 EventListenerManager* nsGlobalWindowOuter::GetOrCreateListenerManager() {
6657 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
6660 EventListenerManager* nsGlobalWindowOuter::GetExistingListenerManager() const {
6661 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
6664 //*****************************************************************************
6665 // nsGlobalWindowOuter::nsPIDOMWindow
6666 //*****************************************************************************
6668 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateParent() {
6669 nsCOMPtr<nsPIDOMWindowOuter> parent = GetParent();
6671 if (this == parent) {
6672 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
6673 if (!chromeElement)
6674 return nullptr; // This is ok, just means a null parent.
6676 Document* doc = chromeElement->GetComposedDoc();
6677 if (!doc) return nullptr; // This is ok, just means a null parent.
6679 return doc->GetWindow();
6682 return parent;
6685 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetPrivateRoot() {
6686 nsCOMPtr<nsPIDOMWindowOuter> top = GetTop();
6688 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
6689 if (chromeElement) {
6690 Document* doc = chromeElement->GetComposedDoc();
6691 if (doc) {
6692 nsCOMPtr<nsPIDOMWindowOuter> parent = doc->GetWindow();
6693 if (parent) {
6694 top = parent->GetTop();
6699 return top;
6702 // This has a caller in Windows-only code (nsNativeAppSupportWin).
6703 Location* nsGlobalWindowOuter::GetLocation() {
6704 // This method can be called on the outer window as well.
6705 FORWARD_TO_INNER(Location, (), nullptr);
6708 void nsGlobalWindowOuter::ActivateOrDeactivate(bool aActivate) {
6709 if (!mDoc) {
6710 return;
6713 // Set / unset mIsActive on the top level window, which is used for the
6714 // :-moz-window-inactive pseudoclass, and its sheet (if any).
6715 nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
6716 nsCOMPtr<nsIWidget> topLevelWidget;
6717 if (mainWidget) {
6718 // Get the top level widget (if the main widget is a sheet, this will
6719 // be the sheet's top (non-sheet) parent).
6720 topLevelWidget = mainWidget->GetSheetWindowParent();
6721 if (!topLevelWidget) {
6722 topLevelWidget = mainWidget;
6726 SetActive(aActivate);
6728 if (mainWidget != topLevelWidget) {
6729 // This is a workaround for the following problem:
6730 // When a window with an open sheet gains or loses focus, only the sheet
6731 // window receives the NS_ACTIVATE/NS_DEACTIVATE event. However the
6732 // styling of the containing top level window also needs to change. We
6733 // get around this by calling nsPIDOMWindow::SetActive() on both windows.
6735 // Get the top level widget's nsGlobalWindowOuter
6736 nsCOMPtr<nsPIDOMWindowOuter> topLevelWindow;
6738 // widgetListener should be a nsXULWindow
6739 nsIWidgetListener* listener = topLevelWidget->GetWidgetListener();
6740 if (listener) {
6741 nsCOMPtr<nsIXULWindow> window = listener->GetXULWindow();
6742 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(window));
6743 topLevelWindow = do_GetInterface(req);
6746 if (topLevelWindow) {
6747 topLevelWindow->SetActive(aActivate);
6752 static bool NotifyDocumentTree(Document* aDocument, void* aData) {
6753 aDocument->EnumerateSubDocuments(NotifyDocumentTree, nullptr);
6754 aDocument->UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, true);
6755 return true;
6758 void nsGlobalWindowOuter::SetActive(bool aActive) {
6759 nsPIDOMWindowOuter::SetActive(aActive);
6760 if (mDoc) {
6761 NotifyDocumentTree(mDoc, nullptr);
6765 bool nsGlobalWindowOuter::IsTopLevelWindowActive() {
6766 nsCOMPtr<nsIDocShellTreeItem> treeItem(GetDocShell());
6767 if (!treeItem) {
6768 return false;
6771 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6772 treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
6773 if (!rootItem) {
6774 return false;
6777 nsCOMPtr<nsPIDOMWindowOuter> domWindow = rootItem->GetWindow();
6778 return domWindow && domWindow->IsActive();
6781 void nsGlobalWindowOuter::SetIsBackground(bool aIsBackground) {
6782 bool changed = aIsBackground != IsBackground();
6783 SetIsBackgroundInternal(aIsBackground);
6785 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
6787 if (inner && changed) {
6788 inner->mTimeoutManager->UpdateBackgroundState();
6791 if (aIsBackground) {
6792 // Notify gamepadManager we are at the background window,
6793 // we need to stop vibrate.
6794 // Stop the vr telemery time spent when it switches to
6795 // the background window.
6796 if (inner && changed) {
6797 inner->StopGamepadHaptics();
6798 inner->StopVRActivity();
6799 // true is for asking to set the delta time to
6800 // the telemetry.
6801 inner->ResetVRTelemetry(true);
6803 return;
6806 if (inner) {
6807 // When switching to be as a top tab, restart the telemetry.
6808 // false is for only resetting the timestamp.
6809 inner->ResetVRTelemetry(false);
6810 inner->SyncGamepadState();
6811 inner->StartVRActivity();
6815 void nsGlobalWindowOuter::SetIsBackgroundInternal(bool aIsBackground) {
6816 if (mIsBackground != aIsBackground) {
6817 TabGroup()->WindowChangedBackgroundStatus(aIsBackground);
6819 mIsBackground = aIsBackground;
6822 void nsGlobalWindowOuter::SetChromeEventHandler(
6823 EventTarget* aChromeEventHandler) {
6824 SetChromeEventHandlerInternal(aChromeEventHandler);
6825 // update the chrome event handler on all our inner windows
6826 RefPtr<nsGlobalWindowInner> inner;
6827 for (PRCList* node = PR_LIST_HEAD(this); node != this;
6828 node = PR_NEXT_LINK(inner)) {
6829 // This cast is only safe if `node != this`, as nsGlobalWindowOuter is also
6830 // in the list.
6831 inner = static_cast<nsGlobalWindowInner*>(node);
6832 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
6833 "bad outer window pointer");
6834 inner->SetChromeEventHandlerInternal(aChromeEventHandler);
6838 void nsGlobalWindowOuter::SetFocusedElement(Element* aElement,
6839 uint32_t aFocusMethod,
6840 bool aNeedsFocus) {
6841 FORWARD_TO_INNER_VOID(SetFocusedElement,
6842 (aElement, aFocusMethod, aNeedsFocus));
6845 uint32_t nsGlobalWindowOuter::GetFocusMethod() {
6846 FORWARD_TO_INNER(GetFocusMethod, (), 0);
6849 bool nsGlobalWindowOuter::ShouldShowFocusRing() {
6850 FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
6853 void nsGlobalWindowOuter::SetKeyboardIndicators(
6854 UIStateChangeType aShowFocusRings) {
6855 nsPIDOMWindowOuter* piWin = GetPrivateRoot();
6856 if (!piWin) {
6857 return;
6860 MOZ_ASSERT(piWin == this);
6862 bool oldShouldShowFocusRing = ShouldShowFocusRing();
6864 // only change the flags that have been modified
6865 nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler);
6866 if (!windowRoot) {
6867 return;
6870 if (aShowFocusRings != UIStateChangeType_NoChange) {
6871 windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set);
6874 nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(this, aShowFocusRings);
6876 bool newShouldShowFocusRing = ShouldShowFocusRing();
6877 if (mInnerWindow && nsGlobalWindowInner::Cast(mInnerWindow)->mHasFocus &&
6878 mInnerWindow->mFocusedElement &&
6879 oldShouldShowFocusRing != newShouldShowFocusRing) {
6880 // Update focusedNode's state.
6881 if (newShouldShowFocusRing) {
6882 mInnerWindow->mFocusedElement->AddStates(NS_EVENT_STATE_FOCUSRING);
6883 } else {
6884 mInnerWindow->mFocusedElement->RemoveStates(NS_EVENT_STATE_FOCUSRING);
6889 bool nsGlobalWindowOuter::TakeFocus(bool aFocus, uint32_t aFocusMethod) {
6890 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
6893 void nsGlobalWindowOuter::SetReadyForFocus() {
6894 FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
6897 void nsGlobalWindowOuter::PageHidden() {
6898 FORWARD_TO_INNER_VOID(PageHidden, ());
6901 already_AddRefed<nsICSSDeclaration>
6902 nsGlobalWindowOuter::GetComputedStyleHelperOuter(Element& aElt,
6903 const nsAString& aPseudoElt,
6904 bool aDefaultStylesOnly) {
6905 if (!mDoc) {
6906 return nullptr;
6909 RefPtr<nsICSSDeclaration> compStyle = NS_NewComputedDOMStyle(
6910 &aElt, aPseudoElt, mDoc,
6911 aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly
6912 : nsComputedDOMStyle::eAll);
6914 return compStyle.forget();
6917 //*****************************************************************************
6918 // nsGlobalWindowOuter::nsIInterfaceRequestor
6919 //*****************************************************************************
6921 nsresult nsGlobalWindowOuter::GetInterfaceInternal(const nsIID& aIID,
6922 void** aSink) {
6923 NS_ENSURE_ARG_POINTER(aSink);
6924 *aSink = nullptr;
6926 if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) {
6927 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6928 webNav.forget(aSink);
6929 } else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
6930 nsCOMPtr<nsIDocShell> docShell = mDocShell;
6931 docShell.forget(aSink);
6933 #ifdef NS_PRINTING
6934 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
6935 if (mDocShell) {
6936 nsCOMPtr<nsIContentViewer> viewer;
6937 mDocShell->GetContentViewer(getter_AddRefs(viewer));
6938 if (viewer) {
6939 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer));
6940 webBrowserPrint.forget(aSink);
6944 #endif
6945 else if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
6946 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(mDocShell));
6947 loadContext.forget(aSink);
6950 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
6953 NS_IMETHODIMP
6954 nsGlobalWindowOuter::GetInterface(const nsIID& aIID, void** aSink) {
6955 nsresult rv = GetInterfaceInternal(aIID, aSink);
6956 if (rv == NS_ERROR_NO_INTERFACE) {
6957 return QueryInterface(aIID, aSink);
6959 return rv;
6962 //*****************************************************************************
6963 // nsGlobalWindowOuter::nsIObserver
6964 //*****************************************************************************
6966 NS_IMETHODIMP
6967 nsGlobalWindowOuter::Observe(nsISupports* aSupports, const char* aTopic,
6968 const char16_t* aData) {
6969 if (!nsCRT::strcmp(aTopic, PERM_CHANGE_NOTIFICATION)) {
6970 nsCOMPtr<nsIPermission> permission = do_QueryInterface(aSupports);
6971 if (!permission) {
6972 return NS_OK;
6974 nsIPrincipal* principal = GetPrincipal();
6975 if (!principal) {
6976 return NS_OK;
6978 if (!AntiTrackingCommon::IsStorageAccessPermission(permission, principal)) {
6979 return NS_OK;
6981 if (!nsCRT::strcmp(aData, u"deleted")) {
6982 // The storage access permission was deleted.
6983 mHasStorageAccess = false;
6984 return NS_OK;
6986 if (!nsCRT::strcmp(aData, u"added") || !nsCRT::strcmp(aData, u"changed")) {
6987 // The storage access permission was granted or modified.
6988 uint32_t expireType = 0;
6989 int64_t expireTime = 0;
6990 MOZ_ALWAYS_SUCCEEDS(permission->GetExpireType(&expireType));
6991 MOZ_ALWAYS_SUCCEEDS(permission->GetExpireTime(&expireTime));
6992 if ((expireType == nsIPermissionManager::EXPIRE_TIME &&
6993 expireTime >= PR_Now() / 1000) ||
6994 (expireType == nsIPermissionManager::EXPIRE_SESSION &&
6995 expireTime != 0)) {
6996 // Permission hasn't expired yet.
6997 mHasStorageAccess = true;
6998 return NS_OK;
7002 return NS_OK;
7005 bool nsGlobalWindowOuter::IsSuspended() const {
7006 MOZ_ASSERT(NS_IsMainThread());
7007 // No inner means we are effectively suspended
7008 if (!mInnerWindow) {
7009 return true;
7011 return mInnerWindow->IsSuspended();
7014 bool nsGlobalWindowOuter::IsFrozen() const {
7015 MOZ_ASSERT(NS_IsMainThread());
7016 // No inner means we are effectively frozen
7017 if (!mInnerWindow) {
7018 return true;
7020 return mInnerWindow->IsFrozen();
7023 nsresult nsGlobalWindowOuter::FireDelayedDOMEvents() {
7024 FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
7027 //*****************************************************************************
7028 // nsGlobalWindowOuter: Window Control Functions
7029 //*****************************************************************************
7031 nsPIDOMWindowOuter* nsGlobalWindowOuter::GetParentInternal() {
7032 nsCOMPtr<nsPIDOMWindowOuter> parent = GetParent();
7034 if (parent && parent != this) {
7035 return parent;
7038 return nullptr;
7041 void nsGlobalWindowOuter::UnblockScriptedClosing() {
7042 mBlockScriptedClosingFlag = false;
7045 class AutoUnblockScriptClosing {
7046 private:
7047 RefPtr<nsGlobalWindowOuter> mWin;
7049 public:
7050 explicit AutoUnblockScriptClosing(nsGlobalWindowOuter* aWin) : mWin(aWin) {
7051 MOZ_ASSERT(mWin);
7053 ~AutoUnblockScriptClosing() {
7054 void (nsGlobalWindowOuter::*run)() =
7055 &nsGlobalWindowOuter::UnblockScriptedClosing;
7056 nsCOMPtr<nsIRunnable> caller = NewRunnableMethod(
7057 "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin, run);
7058 mWin->Dispatch(TaskCategory::Other, caller.forget());
7062 nsresult nsGlobalWindowOuter::OpenInternal(
7063 const nsAString& aUrl, const nsAString& aName, const nsAString& aOptions,
7064 bool aDialog, bool aContentModal, bool aCalledNoScript, bool aDoJSFixups,
7065 bool aNavigate, nsIArray* argv, nsISupports* aExtraArgument,
7066 nsDocShellLoadState* aLoadState, bool aForceNoOpener,
7067 nsPIDOMWindowOuter** aReturn) {
7068 #ifdef DEBUG
7069 uint32_t argc = 0;
7070 if (argv) argv->GetLength(&argc);
7071 #endif
7073 MOZ_ASSERT(!aExtraArgument || (!argv && argc == 0),
7074 "Can't pass in arguments both ways");
7075 MOZ_ASSERT(!aCalledNoScript || (!argv && argc == 0),
7076 "Can't pass JS args when called via the noscript methods");
7078 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
7080 // Calls to window.open from script should navigate.
7081 MOZ_ASSERT(aCalledNoScript || aNavigate);
7083 *aReturn = nullptr;
7085 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
7086 if (!chrome) {
7087 // No chrome means we don't want to go through with this open call
7088 // -- see nsIWindowWatcher.idl
7089 return NS_ERROR_NOT_AVAILABLE;
7092 NS_ASSERTION(mDocShell, "Must have docshell here");
7094 nsAutoCString options;
7095 bool forceNoOpener = aForceNoOpener;
7096 bool forceNoReferrer = false;
7097 // Unlike other window flags, "noopener" comes from splitting on commas with
7098 // HTML whitespace trimming...
7099 nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
7100 aOptions, ',');
7101 while (tok.hasMoreTokens()) {
7102 auto nextTok = tok.nextToken();
7103 if (nextTok.EqualsLiteral("noopener")) {
7104 forceNoOpener = true;
7105 continue;
7107 if (StaticPrefs::dom_window_open_noreferrer_enabled() &&
7108 nextTok.LowerCaseEqualsLiteral("noreferrer")) {
7109 forceNoReferrer = true;
7110 // noreferrer implies noopener
7111 forceNoOpener = true;
7112 continue;
7114 // Want to create a copy of the options without 'noopener' because having
7115 // 'noopener' in the options affects other window features.
7116 if (!options.IsEmpty()) {
7117 options.Append(',');
7119 AppendUTF16toUTF8(nextTok, options);
7122 bool windowExists = WindowExists(aName, forceNoOpener, !aCalledNoScript);
7124 // XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
7125 // (indirectly) maybe we can nix the AutoJSAPI usage OnLinkClickEvent::Run.
7126 // But note that if you change this to GetEntryGlobal(), say, then
7127 // OnLinkClickEvent::Run will need a full-blown AutoEntryScript.
7128 const bool checkForPopup =
7129 !nsContentUtils::LegacyIsCallerChromeOrNativeCode() && !aDialog &&
7130 !windowExists;
7132 // Note: the Void handling here is very important, because the window watcher
7133 // expects a null URL string (not an empty string) if there is no URL to load.
7134 nsCString url;
7135 url.SetIsVoid(true);
7136 nsresult rv = NS_OK;
7138 nsCOMPtr<nsIURI> uri;
7140 // It's important to do this security check before determining whether this
7141 // window opening should be blocked, to ensure that we don't FireAbuseEvents
7142 // for a window opening that wouldn't have succeeded in the first place.
7143 if (!aUrl.IsEmpty()) {
7144 AppendUTF16toUTF8(aUrl, url);
7146 // It's safe to skip the security check below if we're not a dialog
7147 // because window.openDialog is not callable from content script. See bug
7148 // 56851.
7150 // If we're not navigating, we assume that whoever *does* navigate the
7151 // window will do a security check of their own.
7152 if (!url.IsVoid() && !aDialog && aNavigate)
7153 rv = SecurityCheckURL(url.get(), getter_AddRefs(uri));
7156 if (NS_FAILED(rv)) return rv;
7158 PopupBlocker::PopupControlState abuseLevel =
7159 PopupBlocker::GetPopupControlState();
7160 if (checkForPopup) {
7161 abuseLevel = RevisePopupAbuseLevel(abuseLevel);
7162 if (abuseLevel >= PopupBlocker::openBlocked) {
7163 if (!aCalledNoScript) {
7164 // If script in some other window is doing a window.open on us and
7165 // it's being blocked, then it's OK to close us afterwards, probably.
7166 // But if we're doing a window.open on ourselves and block the popup,
7167 // prevent this window from closing until after this script terminates
7168 // so that whatever popup blocker UI the app has will be visible.
7169 nsCOMPtr<nsPIDOMWindowInner> entryWindow =
7170 do_QueryInterface(GetEntryGlobal());
7171 // Note that entryWindow can be null here if some JS component was the
7172 // place where script was entered for this JS execution.
7173 if (entryWindow && entryWindow->GetOuterWindow() == this) {
7174 mBlockScriptedClosingFlag = true;
7175 closeUnblocker.emplace(this);
7179 FireAbuseEvents(aUrl, aName, aOptions);
7180 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
7184 nsCOMPtr<mozIDOMWindowProxy> domReturn;
7186 nsCOMPtr<nsIWindowWatcher> wwatch =
7187 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
7188 NS_ENSURE_TRUE(wwatch, rv);
7190 NS_ConvertUTF16toUTF8 name(aName);
7192 const char* options_ptr = options.IsEmpty() ? nullptr : options.get();
7193 const char* name_ptr = aName.IsEmpty() ? nullptr : name.get();
7195 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
7196 NS_ENSURE_STATE(pwwatch);
7198 MOZ_ASSERT_IF(checkForPopup, abuseLevel < PopupBlocker::openBlocked);
7199 // At this point we should know for a fact that if checkForPopup then
7200 // abuseLevel < PopupBlocker::openBlocked, so we could just check for
7201 // abuseLevel == PopupBlocker::openControlled. But let's be defensive just in
7202 // case and treat anything that fails the above assert as a spam popup too, if
7203 // it ever happens.
7204 bool isPopupSpamWindow =
7205 checkForPopup && (abuseLevel >= PopupBlocker::openControlled);
7208 // Reset popup state while opening a window to prevent the
7209 // current state from being active the whole time a modal
7210 // dialog is open.
7211 AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
7213 if (!aCalledNoScript) {
7214 // We asserted at the top of this function that aNavigate is true for
7215 // !aCalledNoScript.
7216 rv = pwwatch->OpenWindow2(
7217 this, url.IsVoid() ? nullptr : url.get(), name_ptr, options_ptr,
7218 /* aCalledFromScript = */ true, aDialog, aNavigate, argv,
7219 isPopupSpamWindow, forceNoOpener, forceNoReferrer, aLoadState,
7220 getter_AddRefs(domReturn));
7221 } else {
7222 // Force a system caller here so that the window watcher won't screw us
7223 // up. We do NOT want this case looking at the JS context on the stack
7224 // when searching. Compare comments on
7225 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
7227 // Note: Because nsWindowWatcher is so broken, it's actually important
7228 // that we don't force a system caller here, because that screws it up
7229 // when it tries to compute the caller principal to associate with dialog
7230 // arguments. That whole setup just really needs to be rewritten. :-(
7231 Maybe<AutoNoJSAPI> nojsapi;
7232 if (!aContentModal) {
7233 nojsapi.emplace();
7236 rv = pwwatch->OpenWindow2(
7237 this, url.IsVoid() ? nullptr : url.get(), name_ptr, options_ptr,
7238 /* aCalledFromScript = */ false, aDialog, aNavigate, aExtraArgument,
7239 isPopupSpamWindow, forceNoOpener, forceNoReferrer, aLoadState,
7240 getter_AddRefs(domReturn));
7244 NS_ENSURE_SUCCESS(rv, rv);
7246 // success!
7248 if (!aCalledNoScript && !windowExists && uri && !forceNoOpener) {
7249 MaybeAllowStorageForOpenedWindow(uri);
7252 if (domReturn && aDoJSFixups) {
7253 nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(domReturn));
7254 if (!chrome_win) {
7255 // A new non-chrome window was created from a call to
7256 // window.open() from JavaScript, make sure there's a document in
7257 // the new window. We do this by simply asking the new window for
7258 // its document, this will synchronously create an empty document
7259 // if there is no document in the window.
7260 // XXXbz should this just use EnsureInnerWindow()?
7262 // Force document creation.
7263 nsCOMPtr<Document> doc = nsPIDOMWindowOuter::From(domReturn)->GetDoc();
7264 Unused << doc;
7268 *aReturn = do_AddRef(nsPIDOMWindowOuter::From(domReturn)).take();
7269 return NS_OK;
7272 void nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI) {
7273 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
7274 if (NS_WARN_IF(!inner)) {
7275 return;
7278 // No 3rd party URL/window.
7279 if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, aURI)) {
7280 return;
7283 Document* doc = inner->GetDoc();
7284 if (!doc) {
7285 return;
7287 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateCodebasePrincipal(
7288 aURI, doc->NodePrincipal()->OriginAttributesRef());
7290 // We don't care when the asynchronous work finishes here.
7291 Unused << AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
7292 principal, inner, AntiTrackingCommon::eOpener);
7295 //*****************************************************************************
7296 // nsGlobalWindowOuter: Helper Functions
7297 //*****************************************************************************
7299 already_AddRefed<nsIDocShellTreeOwner> nsGlobalWindowOuter::GetTreeOwner() {
7300 // If there's no docShellAsItem, this window must have been closed,
7301 // in that case there is no tree owner.
7303 if (!mDocShell) {
7304 return nullptr;
7307 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
7308 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
7309 return treeOwner.forget();
7312 already_AddRefed<nsIBaseWindow> nsGlobalWindowOuter::GetTreeOwnerWindow() {
7313 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
7315 // If there's no mDocShell, this window must have been closed,
7316 // in that case there is no tree owner.
7318 if (mDocShell) {
7319 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
7322 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
7323 return baseWindow.forget();
7326 already_AddRefed<nsIWebBrowserChrome>
7327 nsGlobalWindowOuter::GetWebBrowserChrome() {
7328 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
7330 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
7331 return browserChrome.forget();
7334 nsIScrollableFrame* nsGlobalWindowOuter::GetScrollFrame() {
7335 if (!mDocShell) {
7336 return nullptr;
7339 PresShell* presShell = mDocShell->GetPresShell();
7340 if (presShell) {
7341 return presShell->GetRootScrollFrameAsScrollable();
7343 return nullptr;
7346 nsresult nsGlobalWindowOuter::SecurityCheckURL(const char* aURL,
7347 nsIURI** aURI) {
7348 nsCOMPtr<nsPIDOMWindowInner> sourceWindow =
7349 do_QueryInterface(GetEntryGlobal());
7350 if (!sourceWindow) {
7351 sourceWindow = GetCurrentInnerWindow();
7353 AutoJSContext cx;
7354 nsGlobalWindowInner* sourceWin = nsGlobalWindowInner::Cast(sourceWindow);
7355 JSAutoRealm ar(cx, sourceWin->GetGlobalJSObject());
7357 // Resolve the baseURI, which could be relative to the calling window.
7359 // Note the algorithm to get the base URI should match the one
7360 // used to actually kick off the load in nsWindowWatcher.cpp.
7361 nsCOMPtr<Document> doc = sourceWindow->GetDoc();
7362 nsIURI* baseURI = nullptr;
7363 auto encoding = UTF_8_ENCODING; // default to utf-8
7364 if (doc) {
7365 baseURI = doc->GetDocBaseURI();
7366 encoding = doc->GetDocumentCharacterSet();
7368 nsCOMPtr<nsIURI> uri;
7369 nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
7370 encoding, baseURI);
7371 if (NS_WARN_IF(NS_FAILED(rv))) {
7372 return NS_ERROR_DOM_SYNTAX_ERR;
7375 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckLoadURIFromScript(
7376 cx, uri))) {
7377 return NS_ERROR_FAILURE;
7380 uri.forget(aURI);
7381 return NS_OK;
7384 void nsGlobalWindowOuter::FlushPendingNotifications(FlushType aType) {
7385 if (mDoc) {
7386 mDoc->FlushPendingNotifications(aType);
7390 void nsGlobalWindowOuter::EnsureSizeAndPositionUpToDate() {
7391 // If we're a subframe, make sure our size is up to date. Make sure to go
7392 // through the document chain rather than the window chain to not flush on
7393 // detached iframes, see bug 1545516.
7394 if (mDoc && mDoc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
7395 RefPtr<Document> parent = mDoc->GetParentDocument();
7396 parent->FlushPendingNotifications(FlushType::Layout);
7400 already_AddRefed<nsISupports> nsGlobalWindowOuter::SaveWindowState() {
7401 if (!mContext || !GetWrapperPreserveColor()) {
7402 // The window may be getting torn down; don't bother saving state.
7403 return nullptr;
7406 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
7407 NS_ASSERTION(inner, "No inner window to save");
7409 // Don't do anything else to this inner window! After this point, all
7410 // calls to SetTimeoutOrInterval will create entries in the timeout
7411 // list that will only run after this window has come out of the bfcache.
7412 // Also, while we're frozen, we won't dispatch online/offline events
7413 // to the page.
7414 inner->Freeze();
7416 nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
7418 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7419 ("saving window state, state = %p", (void*)state));
7421 return state.forget();
7424 nsresult nsGlobalWindowOuter::RestoreWindowState(nsISupports* aState) {
7425 if (!mContext || !GetWrapperPreserveColor()) {
7426 // The window may be getting torn down; don't bother restoring state.
7427 return NS_OK;
7430 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
7431 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
7433 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7434 ("restoring window state, state = %p", (void*)holder));
7436 // And we're ready to go!
7437 nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
7439 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
7440 // it easy to tell which link was last clicked when going back a page.
7441 Element* focusedElement = inner->GetFocusedElement();
7442 if (nsContentUtils::ContentIsLink(focusedElement)) {
7443 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
7444 if (fm) {
7445 // XXXbz Do we need the stack strong ref here?
7446 RefPtr<Element> kungFuDeathGrip(focusedElement);
7447 fm->SetFocus(kungFuDeathGrip, nsIFocusManager::FLAG_NOSCROLL |
7448 nsIFocusManager::FLAG_SHOWRING);
7452 inner->Thaw();
7454 holder->DidRestoreWindow();
7456 return NS_OK;
7459 void nsGlobalWindowOuter::AddSizeOfIncludingThis(
7460 nsWindowSizes& aWindowSizes) const {
7461 aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
7464 uint32_t nsGlobalWindowOuter::GetAutoActivateVRDisplayID() {
7465 uint32_t retVal = mAutoActivateVRDisplayID;
7466 mAutoActivateVRDisplayID = 0;
7467 return retVal;
7470 void nsGlobalWindowOuter::SetAutoActivateVRDisplayID(
7471 uint32_t aAutoActivateVRDisplayID) {
7472 mAutoActivateVRDisplayID = aAutoActivateVRDisplayID;
7475 already_AddRefed<nsWindowRoot> nsGlobalWindowOuter::GetWindowRootOuter() {
7476 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
7477 return root.forget().downcast<nsWindowRoot>();
7480 nsIDOMWindowUtils* nsGlobalWindowOuter::WindowUtils() {
7481 if (!mWindowUtils) {
7482 mWindowUtils = new nsDOMWindowUtils(this);
7484 return mWindowUtils;
7487 // Note: This call will lock the cursor, it will not change as it moves.
7488 // To unlock, the cursor must be set back to Auto.
7489 void nsGlobalWindowOuter::SetCursorOuter(const nsAString& aCursor,
7490 ErrorResult& aError) {
7491 StyleCursorKind cursor;
7493 if (aCursor.EqualsLiteral("auto")) {
7494 cursor = StyleCursorKind::Auto;
7495 } else {
7496 // TODO(emilio): Use Servo for this instead.
7497 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aCursor);
7498 int32_t c;
7499 if (!nsCSSProps::FindKeyword(keyword, nsCSSProps::kCursorKTable, c)) {
7500 return;
7502 cursor = static_cast<StyleCursorKind>(c);
7505 RefPtr<nsPresContext> presContext;
7506 if (mDocShell) {
7507 presContext = mDocShell->GetPresContext();
7510 if (presContext) {
7511 // Need root widget.
7512 PresShell* presShell = mDocShell->GetPresShell();
7513 if (!presShell) {
7514 aError.Throw(NS_ERROR_FAILURE);
7515 return;
7518 nsViewManager* vm = presShell->GetViewManager();
7519 if (!vm) {
7520 aError.Throw(NS_ERROR_FAILURE);
7521 return;
7524 nsView* rootView = vm->GetRootView();
7525 if (!rootView) {
7526 aError.Throw(NS_ERROR_FAILURE);
7527 return;
7530 nsIWidget* widget = rootView->GetNearestWidget(nullptr);
7531 if (!widget) {
7532 aError.Throw(NS_ERROR_FAILURE);
7533 return;
7536 // Call esm and set cursor.
7537 aError = presContext->EventStateManager()->SetCursor(
7538 cursor, nullptr, Nothing(), widget, true);
7542 NS_IMETHODIMP
7543 nsGlobalWindowOuter::GetBrowserDOMWindow(nsIBrowserDOMWindow** aBrowserWindow) {
7544 MOZ_RELEASE_ASSERT(IsChromeWindow());
7545 FORWARD_TO_INNER(GetBrowserDOMWindow, (aBrowserWindow), NS_ERROR_UNEXPECTED);
7548 nsIBrowserDOMWindow* nsGlobalWindowOuter::GetBrowserDOMWindowOuter() {
7549 MOZ_ASSERT(IsChromeWindow());
7550 return mChromeFields.mBrowserDOMWindow;
7553 void nsGlobalWindowOuter::SetBrowserDOMWindowOuter(
7554 nsIBrowserDOMWindow* aBrowserWindow) {
7555 MOZ_ASSERT(IsChromeWindow());
7556 mChromeFields.mBrowserDOMWindow = aBrowserWindow;
7559 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetMessageManager() {
7560 if (!mInnerWindow) {
7561 NS_WARNING("No inner window available!");
7562 return nullptr;
7564 return GetCurrentInnerWindowInternal()->MessageManager();
7567 ChromeMessageBroadcaster* nsGlobalWindowOuter::GetGroupMessageManager(
7568 const nsAString& aGroup) {
7569 if (!mInnerWindow) {
7570 NS_WARNING("No inner window available!");
7571 return nullptr;
7573 return GetCurrentInnerWindowInternal()->GetGroupMessageManager(aGroup);
7576 void nsPIDOMWindowOuter::SetOpenerForInitialContentBrowser(
7577 BrowsingContext* aOpenerWindow) {
7578 MOZ_ASSERT(!mOpenerForInitialContentBrowser,
7579 "Don't set OpenerForInitialContentBrowser twice!");
7580 mOpenerForInitialContentBrowser = aOpenerWindow;
7583 already_AddRefed<BrowsingContext>
7584 nsPIDOMWindowOuter::TakeOpenerForInitialContentBrowser() {
7585 // Intentionally forget our own member
7586 return mOpenerForInitialContentBrowser.forget();
7589 void nsGlobalWindowOuter::InitWasOffline() { mWasOffline = NS_IsOffline(); }
7591 #if defined(MOZ_WIDGET_ANDROID)
7592 int16_t nsGlobalWindowOuter::Orientation(CallerType aCallerType) const {
7593 return nsContentUtils::ResistFingerprinting(aCallerType)
7595 : WindowOrientationObserver::OrientationAngle();
7597 #endif
7599 void nsPIDOMWindowOuter::SetLargeAllocStatus(LargeAllocStatus aStatus) {
7600 MOZ_ASSERT(mLargeAllocStatus == LargeAllocStatus::NONE);
7601 mLargeAllocStatus = aStatus;
7604 bool nsPIDOMWindowOuter::IsTopLevelWindow() {
7605 return nsGlobalWindowOuter::Cast(this)->IsTopLevelWindow();
7608 bool nsPIDOMWindowOuter::HadOriginalOpener() const {
7609 return nsGlobalWindowOuter::Cast(this)->HadOriginalOpener();
7612 void nsGlobalWindowOuter::ReportLargeAllocStatus() {
7613 uint32_t errorFlags = nsIScriptError::warningFlag;
7614 const char* message = nullptr;
7616 switch (mLargeAllocStatus) {
7617 case LargeAllocStatus::SUCCESS:
7618 // Override the error flags such that the success message isn't reported
7619 // as a warning.
7620 errorFlags = nsIScriptError::infoFlag;
7621 message = "LargeAllocationSuccess";
7622 break;
7623 case LargeAllocStatus::NON_WIN32:
7624 errorFlags = nsIScriptError::infoFlag;
7625 message = "LargeAllocationNonWin32";
7626 break;
7627 case LargeAllocStatus::NON_GET:
7628 message = "LargeAllocationNonGetRequest";
7629 break;
7630 case LargeAllocStatus::NON_E10S:
7631 message = "LargeAllocationNonE10S";
7632 break;
7633 case LargeAllocStatus::NOT_ONLY_TOPLEVEL_IN_TABGROUP:
7634 message = "LargeAllocationNotOnlyToplevelInTabGroup";
7635 break;
7636 default: // LargeAllocStatus::NONE
7637 return; // Don't report a message to the console
7640 nsContentUtils::ReportToConsole(errorFlags, NS_LITERAL_CSTRING("DOM"), mDoc,
7641 nsContentUtils::eDOM_PROPERTIES, message);
7644 #if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
7645 # pragma message( \
7646 "wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
7647 # error "Never include unwrapped windows.h in this file!"
7648 #endif
7650 // Helper called by methods that move/resize the window,
7651 // to ensure the presContext (if any) is aware of resolution
7652 // change that may happen in multi-monitor configuration.
7653 void nsGlobalWindowOuter::CheckForDPIChange() {
7654 if (mDocShell) {
7655 RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
7656 if (presContext) {
7657 if (presContext->DeviceContext()->CheckDPIChange()) {
7658 presContext->UIResolutionChanged();
7664 mozilla::dom::TabGroup* nsGlobalWindowOuter::TabGroupOuter() {
7665 // Outer windows lazily join TabGroups when requested. This is usually done
7666 // because a document is getting its NodePrincipal, and asking for the
7667 // TabGroup to determine its DocGroup.
7668 if (!mTabGroup) {
7669 // Get mOpener ourselves, instead of relying on GetOpenerWindowOuter,
7670 // because that way we dodge the LegacyIsCallerChromeOrNativeCode() call
7671 // which we want to return false.
7672 nsCOMPtr<nsPIDOMWindowOuter> piOpener = do_QueryReferent(mOpener);
7673 nsPIDOMWindowOuter* opener = GetSanitizedOpener(piOpener);
7674 nsPIDOMWindowOuter* parent = GetScriptableParentOrNull();
7675 MOZ_ASSERT(!parent || !opener,
7676 "Only one of parent and opener may be provided");
7678 mozilla::dom::TabGroup* toJoin = nullptr;
7679 if (GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome) {
7680 toJoin = TabGroup::GetChromeTabGroup();
7681 } else if (opener) {
7682 toJoin = opener->TabGroup();
7683 } else if (parent) {
7684 toJoin = parent->TabGroup();
7685 } else {
7686 toJoin = TabGroup::GetFromWindow(this);
7689 #ifdef DEBUG
7690 // Make sure that, if we have a tab group from the actor, it matches the one
7691 // we're planning to join.
7692 mozilla::dom::TabGroup* testGroup = TabGroup::GetFromWindow(this);
7693 MOZ_ASSERT_IF(testGroup, testGroup == toJoin);
7694 #endif
7696 mTabGroup = mozilla::dom::TabGroup::Join(this, toJoin);
7698 MOZ_ASSERT(mTabGroup);
7700 #ifdef DEBUG
7701 // Ensure that we don't recurse forever
7702 if (!mIsValidatingTabGroup) {
7703 mIsValidatingTabGroup = true;
7704 // We only need to do this check if we aren't in the chrome tab group
7705 if (mIsChrome) {
7706 MOZ_ASSERT(mTabGroup == TabGroup::GetChromeTabGroup());
7707 } else {
7708 // Sanity check that our tabgroup matches our opener or parent.
7709 RefPtr<nsPIDOMWindowOuter> parent = GetScriptableParentOrNull();
7710 MOZ_ASSERT_IF(parent, parent->TabGroup() == mTabGroup);
7711 nsCOMPtr<nsPIDOMWindowOuter> piOpener = do_QueryReferent(mOpener);
7712 nsPIDOMWindowOuter* opener = GetSanitizedOpener(piOpener);
7713 MOZ_ASSERT_IF(opener && nsGlobalWindowOuter::Cast(opener) != this,
7714 opener->TabGroup() == mTabGroup);
7716 mIsValidatingTabGroup = false;
7718 #endif
7720 return mTabGroup;
7723 nsresult nsGlobalWindowOuter::Dispatch(
7724 TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
7725 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7726 if (GetDocGroup()) {
7727 return GetDocGroup()->Dispatch(aCategory, std::move(aRunnable));
7729 return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
7732 nsISerialEventTarget* nsGlobalWindowOuter::EventTargetFor(
7733 TaskCategory aCategory) const {
7734 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7735 if (GetDocGroup()) {
7736 return GetDocGroup()->EventTargetFor(aCategory);
7738 return DispatcherTrait::EventTargetFor(aCategory);
7741 AbstractThread* nsGlobalWindowOuter::AbstractMainThreadFor(
7742 TaskCategory aCategory) {
7743 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7744 if (GetDocGroup()) {
7745 return GetDocGroup()->AbstractMainThreadFor(aCategory);
7747 return DispatcherTrait::AbstractMainThreadFor(aCategory);
7750 nsGlobalWindowOuter::TemporarilyDisableDialogs::TemporarilyDisableDialogs(
7751 nsGlobalWindowOuter* aWindow MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
7752 : mSavedDialogsEnabled(false) {
7753 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
7755 MOZ_ASSERT(aWindow);
7756 nsGlobalWindowOuter* topWindowOuter = aWindow->GetScriptableTopInternal();
7757 if (!topWindowOuter) {
7758 NS_ERROR(
7759 "nsGlobalWindowOuter::TemporarilyDisableDialogs used without a top "
7760 "window?");
7761 return;
7764 // TODO: Warn if no top window?
7765 nsGlobalWindowInner* topWindow =
7766 topWindowOuter->GetCurrentInnerWindowInternal();
7767 if (topWindow) {
7768 mTopWindow = topWindow;
7769 mSavedDialogsEnabled = mTopWindow->mAreDialogsEnabled;
7770 mTopWindow->mAreDialogsEnabled = false;
7774 nsGlobalWindowOuter::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() {
7775 if (mTopWindow) {
7776 mTopWindow->mAreDialogsEnabled = mSavedDialogsEnabled;
7780 mozilla::dom::TabGroup* nsPIDOMWindowOuter::TabGroup() {
7781 return nsGlobalWindowOuter::Cast(this)->TabGroupOuter();
7784 /* static */
7785 already_AddRefed<nsGlobalWindowOuter> nsGlobalWindowOuter::Create(
7786 nsDocShell* aDocShell, bool aIsChrome) {
7787 uint64_t outerWindowID = aDocShell->GetOuterWindowID();
7788 RefPtr<nsGlobalWindowOuter> window = new nsGlobalWindowOuter(outerWindowID);
7789 if (aIsChrome) {
7790 window->mIsChrome = true;
7792 window->SetDocShell(aDocShell);
7794 window->InitWasOffline();
7795 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
7796 if (obs) {
7797 // Delay calling AddObserver until we hit the event loop, in case we may be
7798 // in the middle of modifying the observer list somehow.
7799 NS_DispatchToMainThread(
7800 NS_NewRunnableFunction("PermChangeDelayRunnable", [obs, window] {
7801 obs->AddObserver(window, PERM_CHANGE_NOTIFICATION, true);
7802 }));
7804 return window.forget();
7807 nsIURI* nsPIDOMWindowOuter::GetDocumentURI() const {
7808 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
7811 void nsPIDOMWindowOuter::MaybeCreateDoc() {
7812 MOZ_ASSERT(!mDoc);
7813 if (nsIDocShell* docShell = GetDocShell()) {
7814 // Note that |document| here is the same thing as our mDoc, but we
7815 // don't have to explicitly set the member variable because the docshell
7816 // has already called SetNewDocument().
7817 nsCOMPtr<Document> document = docShell->GetDocument();
7818 Unused << document;
7822 void nsPIDOMWindowOuter::SetChromeEventHandlerInternal(
7823 EventTarget* aChromeEventHandler) {
7824 // Out-of-line so we don't need to include ContentFrameMessageManager.h in
7825 // nsPIDOMWindow.h.
7826 mChromeEventHandler = aChromeEventHandler;
7828 // mParentTarget and mMessageManager will be set when the next event is
7829 // dispatched or someone asks for our message manager.
7830 mParentTarget = nullptr;
7831 mMessageManager = nullptr;
7834 mozilla::dom::DocGroup* nsPIDOMWindowOuter::GetDocGroup() const {
7835 Document* doc = GetExtantDoc();
7836 if (doc) {
7837 return doc->GetDocGroup();
7839 return nullptr;
7842 nsPIDOMWindowOuter::nsPIDOMWindowOuter(uint64_t aWindowID)
7843 : mFrameElement(nullptr),
7844 mModalStateDepth(0),
7845 mIsActive(false),
7846 mIsBackground(false),
7847 mMediaSuspend(
7848 Preferences::GetBool("media.block-autoplay-until-in-foreground", true)
7849 ? nsISuspendedTypes::SUSPENDED_BLOCK
7850 : nsISuspendedTypes::NONE_SUSPENDED),
7851 mAudioMuted(false),
7852 mAudioVolume(1.0),
7853 mDesktopModeViewport(false),
7854 mIsRootOuterWindow(false),
7855 mInnerWindow(nullptr),
7856 mWindowID(aWindowID),
7857 mMarkedCCGeneration(0),
7858 mServiceWorkersTestingEnabled(false),
7859 mLargeAllocStatus(LargeAllocStatus::NONE) {}
7861 nsPIDOMWindowOuter::~nsPIDOMWindowOuter() {}