Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsGlobalWindow.cpp
blobca6f87291bfdf8fe2a67346865fb6e9410379899
1 /* -*- Mode: C++; tab-width: 2; 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 "nsScreen.h"
16 #include "nsHistory.h"
17 #include "nsPerformance.h"
18 #include "nsDOMNavigationTiming.h"
19 #include "nsIDOMStorageManager.h"
20 #include "mozilla/dom/DOMStorage.h"
21 #include "mozilla/dom/StorageEvent.h"
22 #include "mozilla/dom/StorageEventBinding.h"
23 #include "nsDOMOfflineResourceList.h"
24 #include "nsError.h"
25 #include "nsIIdleService.h"
26 #include "nsISizeOfEventTarget.h"
27 #include "nsDOMJSUtils.h"
28 #include "nsArrayUtils.h"
29 #include "nsIDOMWindowCollection.h"
30 #include "nsDOMWindowList.h"
31 #include "mozilla/dom/WakeLock.h"
32 #include "mozilla/dom/power/PowerManagerService.h"
33 #include "nsIDocShellTreeOwner.h"
34 #include "nsIPermissionManager.h"
35 #include "nsIScriptContext.h"
36 #include "nsIScriptTimeoutHandler.h"
37 #include "nsIController.h"
38 #include "nsScriptNameSpaceManager.h"
39 #include "nsISlowScriptDebug.h"
40 #include "nsWindowMemoryReporter.h"
41 #include "WindowNamedPropertiesHandler.h"
42 #include "nsFrameSelection.h"
43 #include "nsISelectionListener.h"
45 // Helper Classes
46 #include "nsJSUtils.h"
47 #include "jsapi.h" // for JSAutoRequest
48 #include "js/OldDebugAPI.h" // for JS_ClearWatchPointsForObject
49 #include "jswrapper.h"
50 #include "nsReadableUtils.h"
51 #include "nsDOMClassInfo.h"
52 #include "nsJSEnvironment.h"
53 #include "ScriptSettings.h"
54 #include "mozilla/Preferences.h"
55 #include "mozilla/Likely.h"
56 #include "mozilla/unused.h"
58 // Other Classes
59 #include "mozilla/dom/BarProps.h"
60 #include "nsContentCID.h"
61 #include "nsLayoutStatics.h"
62 #include "nsCCUncollectableMarker.h"
63 #include "mozilla/dom/workers/Workers.h"
64 #include "mozilla/dom/MessagePortList.h"
65 #include "mozilla/dom/ToJSValue.h"
66 #include "nsJSPrincipals.h"
67 #include "mozilla/Attributes.h"
68 #include "mozilla/Debug.h"
69 #include "mozilla/EventListenerManager.h"
70 #include "mozilla/EventStates.h"
71 #include "mozilla/MouseEvents.h"
72 #include "AudioChannelService.h"
73 #include "MessageEvent.h"
74 #include "nsAboutProtocolUtils.h"
76 // Interfaces Needed
77 #include "nsIFrame.h"
78 #include "nsCanvasFrame.h"
79 #include "nsIWidget.h"
80 #include "nsIWidgetListener.h"
81 #include "nsIBaseWindow.h"
82 #include "nsIDeviceSensors.h"
83 #include "nsIContent.h"
84 #include "nsIDocShell.h"
85 #include "nsIDocCharset.h"
86 #include "nsIDocument.h"
87 #include "Crypto.h"
88 #ifndef MOZ_DISABLE_CRYPTOLEGACY
89 #include "nsIDOMCryptoLegacy.h"
90 #endif
91 #include "nsIDOMDocument.h"
92 #include "nsIDOMElement.h"
93 #include "nsIDOMEvent.h"
94 #include "nsIDOMOfflineResourceList.h"
95 #include "nsDOMString.h"
96 #include "nsIEmbeddingSiteWindow.h"
97 #include "nsThreadUtils.h"
98 #include "nsILoadContext.h"
99 #include "nsIPresShell.h"
100 #include "nsIScriptSecurityManager.h"
101 #include "nsIScrollableFrame.h"
102 #include "nsView.h"
103 #include "nsViewManager.h"
104 #include "nsISelectionController.h"
105 #include "nsISelection.h"
106 #include "nsIPrompt.h"
107 #include "nsIPromptService.h"
108 #include "nsIPromptFactory.h"
109 #include "nsIWritablePropertyBag2.h"
110 #include "nsIWebNavigation.h"
111 #include "nsIWebBrowserChrome.h"
112 #include "nsIWebBrowserFind.h" // For window.find()
113 #include "nsIWindowMediator.h" // For window.find()
114 #include "nsComputedDOMStyle.h"
115 #include "nsIEntropyCollector.h"
116 #include "nsDOMCID.h"
117 #include "nsDOMWindowUtils.h"
118 #include "nsIWindowWatcher.h"
119 #include "nsPIWindowWatcher.h"
120 #include "nsIContentViewer.h"
121 #include "nsIScriptError.h"
122 #include "nsIControllers.h"
123 #include "nsIControllerContext.h"
124 #include "nsGlobalWindowCommands.h"
125 #include "nsAutoPtr.h"
126 #include "nsContentUtils.h"
127 #include "nsCSSProps.h"
128 #include "nsIDOMFile.h"
129 #include "nsIDOMFileList.h"
130 #include "nsIURIFixup.h"
131 #ifndef DEBUG
132 #include "nsIAppStartup.h"
133 #include "nsToolkitCompsCID.h"
134 #endif
135 #include "nsCDefaultURIFixup.h"
136 #include "mozilla/EventDispatcher.h"
137 #include "mozilla/EventStateManager.h"
138 #include "nsIObserverService.h"
139 #include "nsFocusManager.h"
140 #include "nsIXULWindow.h"
141 #include "nsITimedChannel.h"
142 #include "nsServiceManagerUtils.h"
143 #ifdef MOZ_XUL
144 #include "nsIDOMXULControlElement.h"
145 #include "nsMenuPopupFrame.h"
146 #endif
147 #include "mozilla/dom/CustomEvent.h"
148 #include "nsIFrameRequestCallback.h"
149 #include "nsIJARChannel.h"
151 #include "xpcprivate.h"
153 #ifdef NS_PRINTING
154 #include "nsIPrintSettings.h"
155 #include "nsIPrintSettingsService.h"
156 #include "nsIWebBrowserPrint.h"
157 #endif
159 #include "nsWindowRoot.h"
160 #include "nsNetCID.h"
161 #include "nsIArray.h"
163 // XXX An unfortunate dependency exists here (two XUL files).
164 #include "nsIDOMXULDocument.h"
165 #include "nsIDOMXULCommandDispatcher.h"
167 #include "nsBindingManager.h"
168 #include "nsXBLService.h"
170 // used for popup blocking, needs to be converted to something
171 // belonging to the back-end like nsIContentPolicy
172 #include "nsIPopupWindowManager.h"
174 #include "nsIDragService.h"
175 #include "mozilla/dom/Element.h"
176 #include "mozilla/dom/Selection.h"
177 #include "nsFrameLoader.h"
178 #include "nsISupportsPrimitives.h"
179 #include "nsXPCOMCID.h"
180 #include "mozIThirdPartyUtil.h"
181 #ifdef MOZ_LOGGING
182 // so we can get logging even in release builds
183 #define FORCE_PR_LOG 1
184 #endif
185 #include "prlog.h"
186 #include "prenv.h"
187 #include "prprf.h"
189 #include "mozilla/dom/MessageChannel.h"
190 #include "mozilla/dom/MessagePort.h"
191 #include "mozilla/dom/MessagePortBinding.h"
192 #include "mozilla/dom/indexedDB/IDBFactory.h"
193 #include "mozilla/dom/quota/QuotaManager.h"
195 #include "mozilla/dom/StructuredCloneTags.h"
197 #ifdef MOZ_GAMEPAD
198 #include "mozilla/dom/GamepadService.h"
199 #endif
201 #include "nsRefreshDriver.h"
203 #include "mozilla/dom/SelectionChangeEvent.h"
205 #include "mozilla/Services.h"
206 #include "mozilla/Telemetry.h"
207 #include "nsLocation.h"
208 #include "nsHTMLDocument.h"
209 #include "nsWrapperCacheInlines.h"
210 #include "mozilla/DOMEventTargetHelper.h"
211 #include "prrng.h"
212 #include "nsSandboxFlags.h"
213 #include "TimeChangeObserver.h"
214 #include "mozilla/dom/AudioContext.h"
215 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
216 #include "mozilla/dom/Console.h"
217 #include "mozilla/dom/FunctionBinding.h"
218 #include "mozilla/dom/HashChangeEvent.h"
219 #include "mozilla/dom/MozSelfSupportBinding.h"
220 #include "mozilla/dom/PopStateEvent.h"
221 #include "mozilla/dom/PopupBlockedEvent.h"
222 #include "mozilla/dom/WindowBinding.h"
223 #include "nsITabChild.h"
224 #include "mozilla/dom/MediaQueryList.h"
225 #include "mozilla/dom/ScriptSettings.h"
226 #include "mozilla/dom/NavigatorBinding.h"
227 #ifdef HAVE_SIDEBAR
228 #include "mozilla/dom/ExternalBinding.h"
229 #endif
231 #ifdef MOZ_WEBSPEECH
232 #include "mozilla/dom/SpeechSynthesis.h"
233 #endif
235 #ifdef MOZ_B2G
236 #include "nsPISocketTransportService.h"
237 #endif
239 // Apple system headers seem to have a check() macro. <sigh>
240 #ifdef check
241 class nsIScriptTimeoutHandler;
242 #undef check
243 #endif // check
244 #include "AccessCheck.h"
246 #ifdef ANDROID
247 #include <android/log.h>
248 #endif
250 #ifdef PR_LOGGING
251 static PRLogModuleInfo* gDOMLeakPRLog;
252 #endif
254 #ifdef XP_WIN
255 #include <process.h>
256 #define getpid _getpid
257 #else
258 #include <unistd.h> // for getpid()
259 #endif
261 static const char kStorageEnabled[] = "dom.storage.enabled";
263 using namespace mozilla;
264 using namespace mozilla::dom;
265 using namespace mozilla::dom::ipc;
266 using mozilla::TimeStamp;
267 using mozilla::TimeDuration;
269 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
270 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
271 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
273 static nsIEntropyCollector *gEntropyCollector = nullptr;
274 static int32_t gRefCnt = 0;
275 static int32_t gOpenPopupSpamCount = 0;
276 static PopupControlState gPopupControlState = openAbused;
277 static int32_t gRunningTimeoutDepth = 0;
278 static bool gMouseDown = false;
279 static bool gDragServiceDisabled = false;
280 static bool gSelectionCaretPrefEnabled = false;
281 static FILE *gDumpFile = nullptr;
282 static uint64_t gNextWindowID = 0;
283 static uint32_t gSerialCounter = 0;
284 static uint32_t gTimeoutsRecentlySet = 0;
285 static TimeStamp gLastRecordedRecentTimeouts;
286 #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
288 #ifdef DEBUG_jst
289 int32_t gTimeoutCnt = 0;
290 #endif
292 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
293 #define DEBUG_PAGE_CACHE
294 #endif
296 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
298 // The default shortest interval/timeout we permit
299 #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
300 #define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
301 static int32_t gMinTimeoutValue;
302 static int32_t gMinBackgroundTimeoutValue;
303 inline int32_t
304 nsGlobalWindow::DOMMinTimeoutValue() const {
305 bool isBackground = !mOuterWindow || mOuterWindow->IsBackground();
306 return
307 std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, 0);
310 // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
311 // uses 5.
312 #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
314 // The longest interval (as PRIntervalTime) we permit, or that our
315 // timer code can handle, really. See DELAY_INTERVAL_LIMIT in
316 // nsTimerImpl.h for details.
317 #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT
319 #define FORWARD_TO_OUTER(method, args, err_rval) \
320 PR_BEGIN_MACRO \
321 if (IsInnerWindow()) { \
322 nsGlobalWindow *outer = GetOuterWindowInternal(); \
323 if (!HasActiveDocument()) { \
324 NS_WARNING(outer ? \
325 "Inner window does not have active document." : \
326 "No outer window available!"); \
327 return err_rval; \
329 return outer->method args; \
331 PR_END_MACRO
333 #define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \
334 PR_BEGIN_MACRO \
335 if (IsInnerWindow()) { \
336 nsGlobalWindow *outer = GetOuterWindowInternal(); \
337 if (!HasActiveDocument()) { \
338 if (!outer) { \
339 NS_WARNING("No outer window available!"); \
340 errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \
341 } else { \
342 errorresult.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
344 } else { \
345 return outer->method args; \
347 return err_rval; \
349 PR_END_MACRO
351 #define FORWARD_TO_OUTER_VOID(method, args) \
352 PR_BEGIN_MACRO \
353 if (IsInnerWindow()) { \
354 nsGlobalWindow *outer = GetOuterWindowInternal(); \
355 if (!HasActiveDocument()) { \
356 NS_WARNING(outer ? \
357 "Inner window does not have active document." : \
358 "No outer window available!"); \
359 return; \
361 outer->method args; \
362 return; \
364 PR_END_MACRO
366 #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \
367 PR_BEGIN_MACRO \
368 if (IsInnerWindow()) { \
369 nsGlobalWindow *outer = GetOuterWindowInternal(); \
370 if (!HasActiveDocument()) { \
371 NS_WARNING(outer ? \
372 "Inner window does not have active document." : \
373 "No outer window available!"); \
374 return err_rval; \
376 return ((nsGlobalChromeWindow *)outer)->method args; \
378 PR_END_MACRO
380 #define FORWARD_TO_INNER_CHROME(method, args, err_rval) \
381 PR_BEGIN_MACRO \
382 if (IsOuterWindow()) { \
383 if (!mInnerWindow) { \
384 NS_WARNING("No inner window available!"); \
385 return err_rval; \
387 return ((nsGlobalChromeWindow *)mInnerWindow)->method args; \
389 PR_END_MACRO
391 #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \
392 PR_BEGIN_MACRO \
393 if (IsInnerWindow()) { \
394 nsGlobalWindow *outer = GetOuterWindowInternal(); \
395 if (!HasActiveDocument()) { \
396 NS_WARNING(outer ? \
397 "Inner window does not have active document." : \
398 "No outer window available!"); \
399 return err_rval; \
401 return ((nsGlobalModalWindow *)outer)->method args; \
403 PR_END_MACRO
405 #define FORWARD_TO_INNER(method, args, err_rval) \
406 PR_BEGIN_MACRO \
407 if (IsOuterWindow()) { \
408 if (!mInnerWindow) { \
409 NS_WARNING("No inner window available!"); \
410 return err_rval; \
412 return GetCurrentInnerWindowInternal()->method args; \
414 PR_END_MACRO
416 #define FORWARD_TO_INNER_OR_THROW(method, args, errorresult, err_rval) \
417 PR_BEGIN_MACRO \
418 if (IsOuterWindow()) { \
419 if (!mInnerWindow) { \
420 NS_WARNING("No inner window available!"); \
421 errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \
422 return err_rval; \
424 return GetCurrentInnerWindowInternal()->method args; \
426 PR_END_MACRO
428 #define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \
429 PR_BEGIN_MACRO \
430 if (IsOuterWindow()) { \
431 if (!mInnerWindow) { \
432 NS_WARNING("No inner window available!"); \
433 return err_rval; \
435 return ((nsGlobalModalWindow*)GetCurrentInnerWindowInternal())->method args; \
437 PR_END_MACRO
439 #define FORWARD_TO_INNER_VOID(method, args) \
440 PR_BEGIN_MACRO \
441 if (IsOuterWindow()) { \
442 if (!mInnerWindow) { \
443 NS_WARNING("No inner window available!"); \
444 return; \
446 GetCurrentInnerWindowInternal()->method args; \
447 return; \
449 PR_END_MACRO
451 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an
452 // inner doesn't already exists.
453 #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \
454 PR_BEGIN_MACRO \
455 if (IsOuterWindow()) { \
456 if (!mInnerWindow) { \
457 if (mIsClosed) { \
458 return err_rval; \
460 nsCOMPtr<nsIDOMDocument> doc; \
461 nsresult fwdic_nr = GetDocument(getter_AddRefs(doc)); \
462 NS_ENSURE_SUCCESS(fwdic_nr, err_rval); \
463 if (!mInnerWindow) { \
464 return err_rval; \
467 return GetCurrentInnerWindowInternal()->method args; \
469 PR_END_MACRO
471 // CIDs
472 static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
474 static const char sPopStatePrefStr[] = "browser.history.allowPopState";
476 #define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload")
477 #define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload")
480 * An indirect observer object that means we don't have to implement nsIObserver
481 * on nsGlobalWindow, where any script could see it.
483 class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver,
484 public nsIInterfaceRequestor
486 public:
487 explicit nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {}
488 NS_DECL_ISUPPORTS
489 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
491 if (!mWindow)
492 return NS_OK;
493 return mWindow->Observe(aSubject, aTopic, aData);
495 void Forget() { mWindow = nullptr; }
496 NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult)
498 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
499 return mWindow->QueryInterface(aIID, aResult);
501 return NS_NOINTERFACE;
504 private:
505 ~nsGlobalWindowObserver() {}
507 nsGlobalWindow* mWindow;
510 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
512 nsTimeout::nsTimeout()
513 : mCleared(false),
514 mRunning(false),
515 mIsInterval(false),
516 mPublicId(0),
517 mInterval(0),
518 mFiringDepth(0),
519 mNestingLevel(0),
520 mPopupState(openAllowed)
522 #ifdef DEBUG_jst
524 extern int gTimeoutCnt;
526 ++gTimeoutCnt;
528 #endif
530 MOZ_COUNT_CTOR(nsTimeout);
533 nsTimeout::~nsTimeout()
535 #ifdef DEBUG_jst
537 extern int gTimeoutCnt;
539 --gTimeoutCnt;
541 #endif
543 if (mTimer) {
544 mTimer->Cancel();
545 mTimer = nullptr;
548 MOZ_COUNT_DTOR(nsTimeout);
551 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout)
553 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout)
554 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout)
555 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
556 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
557 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler)
558 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
559 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef)
560 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release)
562 // Return true if this timeout has a refcount of 1. This is used to check
563 // that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.
564 bool
565 nsTimeout::HasRefCntOne()
567 return mRefCnt.get() == 1;
570 nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
571 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
572 mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
573 mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
574 mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
575 mMayHaveTouchCaret(false), mMayHaveMouseEnterLeaveEventListener(false),
576 mMayHavePointerEnterLeaveEventListener(false),
577 mIsModalContentWindow(false),
578 mIsActive(false), mIsBackground(false),
579 mAudioMuted(false), mAudioVolume(1.0),
580 mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
581 // Make sure no actual window ends up with mWindowID == 0
582 mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
583 mMarkedCCGeneration(0), mSendAfterRemotePaint(false)
586 nsPIDOMWindow::~nsPIDOMWindow() {}
588 // DialogValueHolder CC goop.
589 NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue)
591 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
592 NS_INTERFACE_MAP_ENTRY(nsISupports)
593 NS_INTERFACE_MAP_END
595 NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder)
596 NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder)
598 //*****************************************************************************
599 // nsOuterWindowProxy: Outer Window Proxy
600 //*****************************************************************************
602 class nsOuterWindowProxy : public js::Wrapper
604 public:
605 MOZ_CONSTEXPR nsOuterWindowProxy() : js::Wrapper(0) { }
607 virtual bool finalizeInBackground(JS::Value priv) const MOZ_OVERRIDE {
608 return false;
611 virtual const char *className(JSContext *cx,
612 JS::Handle<JSObject*> wrapper) const MOZ_OVERRIDE;
613 virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
615 // Fundamental traps
616 virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
617 const MOZ_OVERRIDE;
618 virtual bool preventExtensions(JSContext *cx,
619 JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
620 virtual bool getPropertyDescriptor(JSContext* cx,
621 JS::Handle<JSObject*> proxy,
622 JS::Handle<jsid> id,
623 JS::MutableHandle<JSPropertyDescriptor> desc)
624 const MOZ_OVERRIDE;
625 virtual bool getOwnPropertyDescriptor(JSContext* cx,
626 JS::Handle<JSObject*> proxy,
627 JS::Handle<jsid> id,
628 JS::MutableHandle<JSPropertyDescriptor> desc)
629 const MOZ_OVERRIDE;
630 virtual bool defineProperty(JSContext* cx,
631 JS::Handle<JSObject*> proxy,
632 JS::Handle<jsid> id,
633 JS::MutableHandle<JSPropertyDescriptor> desc)
634 const MOZ_OVERRIDE;
635 virtual bool getOwnPropertyNames(JSContext *cx,
636 JS::Handle<JSObject*> proxy,
637 JS::AutoIdVector &props) const MOZ_OVERRIDE;
638 virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
639 JS::Handle<jsid> id,
640 bool *bp) const MOZ_OVERRIDE;
641 virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
642 JS::AutoIdVector &props) const MOZ_OVERRIDE;
644 virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy,
645 JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const MOZ_OVERRIDE;
646 virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
647 JS::Handle<jsid> id) const MOZ_OVERRIDE;
649 // Derived traps
650 virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
651 JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
652 virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
653 JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
654 virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
655 JS::Handle<JSObject*> receiver,
656 JS::Handle<jsid> id,
657 JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
658 virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
659 JS::Handle<JSObject*> receiver,
660 JS::Handle<jsid> id,
661 bool strict,
662 JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
663 virtual bool keys(JSContext *cx, JS::Handle<JSObject*> proxy,
664 JS::AutoIdVector &props) const MOZ_OVERRIDE;
665 virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
666 unsigned flags,
667 JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
669 static const nsOuterWindowProxy singleton;
671 protected:
672 nsGlobalWindow* GetWindow(JSObject *proxy) const
674 return nsGlobalWindow::FromSupports(
675 static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
678 // False return value means we threw an exception. True return value
679 // but false "found" means we didn't have a subframe at that index.
680 bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy,
681 JS::Handle<jsid> id,
682 JS::MutableHandle<JS::Value> vp,
683 bool &found) const;
685 // Returns a non-null window only if id is an index and we have a
686 // window at that index.
687 already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx,
688 JS::Handle<JSObject*> proxy,
689 JS::Handle<jsid> id) const;
691 bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
692 JS::AutoIdVector &props) const;
695 const js::Class OuterWindowProxyClass =
696 PROXY_CLASS_WITH_EXT(
697 "Proxy",
698 0, /* additional slots */
699 0, /* additional class flags */
700 nullptr, /* call */
701 nullptr, /* construct */
702 PROXY_MAKE_EXT(
703 nullptr, /* outerObject */
704 js::proxy_innerObject,
705 nullptr, /* iteratorObject */
706 false /* isWrappedNative */
709 bool
710 nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
711 bool *extensible) const
713 // If [[Extensible]] could be false, then navigating a window could navigate
714 // to a window that's [[Extensible]] after being at one that wasn't: an
715 // invariant violation. So always report true for this.
716 *extensible = true;
717 return true;
720 bool
721 nsOuterWindowProxy::preventExtensions(JSContext *cx,
722 JS::Handle<JSObject*> proxy) const
724 // See above.
725 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
726 JSMSG_CANT_CHANGE_EXTENSIBILITY);
727 return false;
730 const char *
731 nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) const
733 MOZ_ASSERT(js::IsProxy(proxy));
735 return "Window";
738 void
739 nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) const
741 nsGlobalWindow* global = GetWindow(proxy);
742 if (global) {
743 global->ClearWrapper();
745 // Ideally we would use OnFinalize here, but it's possible that
746 // EnsureScriptEnvironment will later be called on the window, and we don't
747 // want to create a new script object in that case. Therefore, we need to
748 // write a non-null value that will reliably crash when dereferenced.
749 global->PoisonOuterWindowProxy(proxy);
753 bool
754 nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx,
755 JS::Handle<JSObject*> proxy,
756 JS::Handle<jsid> id,
757 JS::MutableHandle<JSPropertyDescriptor> desc) const
759 // The only thing we can do differently from js::Wrapper is shadow stuff with
760 // our indexed properties, so we can just try getOwnPropertyDescriptor and if
761 // that gives us nothing call on through to js::Wrapper.
762 desc.object().set(nullptr);
763 if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) {
764 return false;
767 if (desc.object()) {
768 return true;
771 return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc);
774 bool
775 nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx,
776 JS::Handle<JSObject*> proxy,
777 JS::Handle<jsid> id,
778 JS::MutableHandle<JSPropertyDescriptor> desc)
779 const
781 bool found;
782 if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) {
783 return false;
785 if (found) {
786 FillPropertyDescriptor(desc, proxy, true);
787 return true;
789 // else fall through to js::Wrapper
791 return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
794 bool
795 nsOuterWindowProxy::defineProperty(JSContext* cx,
796 JS::Handle<JSObject*> proxy,
797 JS::Handle<jsid> id,
798 JS::MutableHandle<JSPropertyDescriptor> desc)
799 const
801 int32_t index = GetArrayIndexFromId(cx, id);
802 if (IsArrayIndex(index)) {
803 // Spec says to Reject whether this is a supported index or not,
804 // since we have no indexed setter or indexed creator. That means
805 // throwing in strict mode (FIXME: Bug 828137), doing nothing in
806 // non-strict mode.
807 return true;
810 return js::Wrapper::defineProperty(cx, proxy, id, desc);
813 bool
814 nsOuterWindowProxy::getOwnPropertyNames(JSContext *cx,
815 JS::Handle<JSObject*> proxy,
816 JS::AutoIdVector &props) const
818 // Just our indexed stuff followed by our "normal" own property names.
819 if (!AppendIndexedPropertyNames(cx, proxy, props)) {
820 return false;
823 JS::AutoIdVector innerProps(cx);
824 if (!js::Wrapper::getOwnPropertyNames(cx, proxy, innerProps)) {
825 return false;
827 return js::AppendUnique(cx, props, innerProps);
830 bool
831 nsOuterWindowProxy::delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
832 JS::Handle<jsid> id, bool *bp) const
834 if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
835 // Reject (which means throw if strict, else return false) the delete.
836 // Except we don't even know whether we're strict. See bug 803157.
837 *bp = false;
838 return true;
841 int32_t index = GetArrayIndexFromId(cx, id);
842 if (IsArrayIndex(index)) {
843 // Indexed, but not supported. Spec says return true.
844 *bp = true;
845 return true;
848 return js::Wrapper::delete_(cx, proxy, id, bp);
851 bool
852 nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
853 JS::AutoIdVector &props) const
855 // Just our indexed stuff followed by our "normal" own property names.
856 if (!AppendIndexedPropertyNames(cx, proxy, props)) {
857 return false;
860 JS::AutoIdVector innerProps(cx);
861 if (!js::Wrapper::enumerate(cx, proxy, innerProps)) {
862 return false;
864 return js::AppendUnique(cx, props, innerProps);
867 bool
868 nsOuterWindowProxy::has(JSContext *cx, JS::Handle<JSObject*> proxy,
869 JS::Handle<jsid> id, bool *bp) const
871 if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
872 *bp = true;
873 return true;
876 return js::Wrapper::has(cx, proxy, id, bp);
879 bool
880 nsOuterWindowProxy::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
881 JS::Handle<jsid> id, bool *bp) const
883 if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
884 *bp = true;
885 return true;
888 return js::Wrapper::hasOwn(cx, proxy, id, bp);
891 bool
892 nsOuterWindowProxy::get(JSContext *cx, JS::Handle<JSObject*> proxy,
893 JS::Handle<JSObject*> receiver,
894 JS::Handle<jsid> id,
895 JS::MutableHandle<JS::Value> vp) const
897 if (id == nsDOMClassInfo::sWrappedJSObject_id &&
898 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
899 vp.set(JS::ObjectValue(*proxy));
900 return true;
903 bool found;
904 if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
905 return false;
907 if (found) {
908 return true;
910 // Else fall through to js::Wrapper
912 return js::Wrapper::get(cx, proxy, receiver, id, vp);
915 bool
916 nsOuterWindowProxy::set(JSContext *cx, JS::Handle<JSObject*> proxy,
917 JS::Handle<JSObject*> receiver,
918 JS::Handle<jsid> id,
919 bool strict,
920 JS::MutableHandle<JS::Value> vp) const
922 int32_t index = GetArrayIndexFromId(cx, id);
923 if (IsArrayIndex(index)) {
924 // Reject (which means throw if and only if strict) the set.
925 if (strict) {
926 // XXXbz This needs to throw, but see bug 828137.
928 return true;
931 return js::Wrapper::set(cx, proxy, receiver, id, strict, vp);
934 bool
935 nsOuterWindowProxy::keys(JSContext *cx, JS::Handle<JSObject*> proxy,
936 JS::AutoIdVector &props) const
938 // BaseProxyHandler::keys seems to do what we want here: call
939 // getOwnPropertyNames and then filter out the non-enumerable properties.
940 return js::BaseProxyHandler::keys(cx, proxy, props);
943 bool
944 nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
945 unsigned flags, JS::MutableHandle<JS::Value> vp) const
947 // BaseProxyHandler::iterate seems to do what we want here: fall
948 // back on the property names returned from keys() and enumerate().
949 return js::BaseProxyHandler::iterate(cx, proxy, flags, vp);
952 bool
953 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
954 JS::Handle<JSObject*> proxy,
955 JS::Handle<jsid> id,
956 JS::MutableHandle<JS::Value> vp,
957 bool& found) const
959 nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id);
960 if (!frame) {
961 found = false;
962 return true;
965 found = true;
966 // Just return the window's global
967 nsGlobalWindow* global = static_cast<nsGlobalWindow*>(frame.get());
968 global->EnsureInnerWindow();
969 JSObject* obj = global->FastGetGlobalJSObject();
970 // This null check fixes a hard-to-reproduce crash that occurs when we
971 // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
972 // comment 105.
973 if (MOZ_UNLIKELY(!obj)) {
974 return xpc::Throw(cx, NS_ERROR_FAILURE);
977 vp.setObject(*obj);
978 return JS_WrapValue(cx, vp);
981 already_AddRefed<nsIDOMWindow>
982 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
983 JS::Handle<JSObject*> proxy,
984 JS::Handle<jsid> id) const
986 int32_t index = GetArrayIndexFromId(cx, id);
987 if (!IsArrayIndex(index)) {
988 return nullptr;
991 nsGlobalWindow* win = GetWindow(proxy);
992 bool unused;
993 return win->IndexedGetter(index, unused);
996 bool
997 nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
998 JS::AutoIdVector &props) const
1000 uint32_t length = GetWindow(proxy)->Length();
1001 MOZ_ASSERT(int32_t(length) >= 0);
1002 if (!props.reserve(props.length() + length)) {
1003 return false;
1005 for (int32_t i = 0; i < int32_t(length); ++i) {
1006 props.append(INT_TO_JSID(i));
1009 return true;
1012 bool
1013 nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy,
1014 JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const
1016 return js::WatchGuts(cx, proxy, id, callable);
1019 bool
1020 nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
1021 JS::Handle<jsid> id) const
1023 return js::UnwatchGuts(cx, proxy, id);
1026 const nsOuterWindowProxy
1027 nsOuterWindowProxy::singleton;
1029 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
1031 public:
1032 MOZ_CONSTEXPR nsChromeOuterWindowProxy() : nsOuterWindowProxy() { }
1034 virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) const MOZ_OVERRIDE;
1036 static const nsChromeOuterWindowProxy singleton;
1039 const char *
1040 nsChromeOuterWindowProxy::className(JSContext *cx,
1041 JS::Handle<JSObject*> proxy) const
1043 MOZ_ASSERT(js::IsProxy(proxy));
1045 return "ChromeWindow";
1048 const nsChromeOuterWindowProxy
1049 nsChromeOuterWindowProxy::singleton;
1051 static JSObject*
1052 NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> parent, bool isChrome)
1054 JSAutoCompartment ac(cx, parent);
1055 js::WrapperOptions options;
1056 options.setClass(&OuterWindowProxyClass);
1057 options.setSingleton(true);
1058 JSObject *obj = js::Wrapper::New(cx, parent, parent,
1059 isChrome ? &nsChromeOuterWindowProxy::singleton
1060 : &nsOuterWindowProxy::singleton,
1061 &options);
1063 NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class");
1064 return obj;
1067 //*****************************************************************************
1068 //*** nsGlobalWindow: Object Management
1069 //*****************************************************************************
1071 nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
1072 : nsPIDOMWindow(aOuterWindow),
1073 mIdleFuzzFactor(0),
1074 mIdleCallbackIndex(-1),
1075 mCurrentlyIdle(false),
1076 mAddActiveEventFuzzTime(true),
1077 mIsFrozen(false),
1078 mFullScreen(false),
1079 mIsClosed(false),
1080 mInClose(false),
1081 mHavePendingClose(false),
1082 mHadOriginalOpener(false),
1083 mIsPopupSpam(false),
1084 mBlockScriptedClosingFlag(false),
1085 mFireOfflineStatusChangeEventOnThaw(false),
1086 mNotifyIdleObserversIdleOnThaw(false),
1087 mNotifyIdleObserversActiveOnThaw(false),
1088 mCreatingInnerWindow(false),
1089 mIsChrome(false),
1090 mCleanMessageManager(false),
1091 mNeedsFocus(true),
1092 mHasFocus(false),
1093 #if defined(XP_MACOSX)
1094 mShowAccelerators(false),
1095 mShowFocusRings(false),
1096 #else
1097 mShowAccelerators(true),
1098 mShowFocusRings(true),
1099 #endif
1100 mShowFocusRingForContent(false),
1101 mFocusByKeyOccurred(false),
1102 mInnerObjectsFreed(false),
1103 mHasGamepad(false),
1104 #ifdef MOZ_GAMEPAD
1105 mHasSeenGamepadInput(false),
1106 #endif
1107 mNotifiedIDDestroyed(false),
1108 mAllowScriptsToClose(false),
1109 mTimeoutInsertionPoint(nullptr),
1110 mTimeoutPublicIdCounter(1),
1111 mTimeoutFiringDepth(0),
1112 mTimeoutsSuspendDepth(0),
1113 mFocusMethod(0),
1114 mSerial(0),
1115 #ifdef DEBUG
1116 mSetOpenerWindowCalled(false),
1117 #endif
1118 #ifdef MOZ_B2G
1119 mNetworkUploadObserverEnabled(false),
1120 mNetworkDownloadObserverEnabled(false),
1121 #endif
1122 mCleanedUp(false),
1123 mDialogAbuseCount(0),
1124 mAreDialogsEnabled(true)
1126 nsLayoutStatics::AddRef();
1128 // Initialize the PRCList (this).
1129 PR_INIT_CLIST(this);
1131 if (Preferences::GetBool("dom.window_experimental_bindings") ||
1132 !aOuterWindow) {
1133 SetIsDOMBinding();
1136 if (aOuterWindow) {
1137 // |this| is an inner window, add this inner window to the outer
1138 // window list of inners.
1139 PR_INSERT_AFTER(this, aOuterWindow);
1141 mObserver = new nsGlobalWindowObserver(this);
1142 if (mObserver) {
1143 NS_ADDREF(mObserver);
1144 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1145 if (os) {
1146 // Watch for online/offline status changes so we can fire events. Use
1147 // a strong reference.
1148 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1149 false);
1151 // Watch for dom-storage2-changed so we can fire storage
1152 // events. Use a strong reference.
1153 os->AddObserver(mObserver, "dom-storage2-changed", false);
1156 Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
1158 } else {
1159 // |this| is an outer window. Outer windows start out frozen and
1160 // remain frozen until they get an inner window, so freeze this
1161 // outer window here.
1162 Freeze();
1164 mObserver = nullptr;
1167 // We could have failed the first time through trying
1168 // to create the entropy collector, so we should
1169 // try to get one until we succeed.
1171 gRefCnt++;
1173 if (gRefCnt == 1) {
1174 Preferences::AddIntVarCache(&gMinTimeoutValue,
1175 "dom.min_timeout_value",
1176 DEFAULT_MIN_TIMEOUT_VALUE);
1177 Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
1178 "dom.min_background_timeout_value",
1179 DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
1180 Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled,
1181 "dom.idle-observers-api.fuzz_time.disabled",
1182 false);
1183 Preferences::AddBoolVarCache(&gSelectionCaretPrefEnabled,
1184 "selectioncaret.enabled",
1185 false);
1188 if (gDumpFile == nullptr) {
1189 const nsAdoptingCString& fname =
1190 Preferences::GetCString("browser.dom.window.dump.file");
1191 if (!fname.IsEmpty()) {
1192 // if this fails to open, Dump() knows to just go to stdout
1193 // on null.
1194 gDumpFile = fopen(fname, "wb+");
1195 } else {
1196 gDumpFile = stdout;
1200 mSerial = ++gSerialCounter;
1202 #ifdef DEBUG
1203 if (!PR_GetEnv("MOZ_QUIET")) {
1204 printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1205 gRefCnt,
1206 static_cast<void*>(ToCanonicalSupports(this)),
1207 getpid(),
1208 gSerialCounter,
1209 static_cast<void*>(ToCanonicalSupports(aOuterWindow)));
1211 #endif
1213 #ifdef PR_LOGGING
1214 if (gDOMLeakPRLog)
1215 PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
1216 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
1217 #endif
1219 NS_ASSERTION(sWindowsById, "Windows hash table must be created!");
1220 NS_ASSERTION(!sWindowsById->Get(mWindowID),
1221 "This window shouldn't be in the hash table yet!");
1222 // We seem to see crashes in release builds because of null |sWindowsById|.
1223 if (sWindowsById) {
1224 sWindowsById->Put(mWindowID, this);
1228 /* static */
1229 void
1230 nsGlobalWindow::Init()
1232 CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
1233 NS_ASSERTION(gEntropyCollector,
1234 "gEntropyCollector should have been initialized!");
1236 #ifdef PR_LOGGING
1237 gDOMLeakPRLog = PR_NewLogModule("DOMLeak");
1238 NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");
1239 #endif
1241 sWindowsById = new WindowByIdTable();
1244 static PLDHashOperator
1245 DisconnectEventTargetObjects(nsPtrHashKey<DOMEventTargetHelper>* aKey,
1246 void* aClosure)
1248 nsRefPtr<DOMEventTargetHelper> target = aKey->GetKey();
1249 target->DisconnectFromOwner();
1250 return PL_DHASH_NEXT;
1253 nsGlobalWindow::~nsGlobalWindow()
1255 mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
1256 mEventTargetObjects.Clear();
1258 // We have to check if sWindowsById isn't null because ::Shutdown might have
1259 // been called.
1260 if (sWindowsById) {
1261 NS_ASSERTION(sWindowsById->Get(mWindowID),
1262 "This window should be in the hash table");
1263 sWindowsById->Remove(mWindowID);
1266 --gRefCnt;
1268 #ifdef DEBUG
1269 if (!PR_GetEnv("MOZ_QUIET")) {
1270 nsAutoCString url;
1271 if (mLastOpenedURI) {
1272 mLastOpenedURI->GetSpec(url);
1274 // Data URLs can be very long, so truncate to avoid flooding the log.
1275 const uint32_t maxURLLength = 1000;
1276 if (url.Length() > maxURLLength) {
1277 url.Truncate(maxURLLength);
1281 nsGlobalWindow* outer = static_cast<nsGlobalWindow*>(mOuterWindow.get());
1282 printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n",
1283 gRefCnt,
1284 static_cast<void*>(ToCanonicalSupports(this)),
1285 getpid(),
1286 mSerial,
1287 static_cast<void*>(ToCanonicalSupports(outer)),
1288 url.get());
1290 #endif
1292 #ifdef PR_LOGGING
1293 if (gDOMLeakPRLog)
1294 PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
1295 ("DOMWINDOW %p destroyed", this));
1296 #endif
1298 if (IsOuterWindow()) {
1299 JSObject *proxy = GetWrapperPreserveColor();
1300 if (proxy) {
1301 js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr));
1304 // An outer window is destroyed with inner windows still possibly
1305 // alive, iterate through the inner windows and null out their
1306 // back pointer to this outer, and pull them out of the list of
1307 // inner windows.
1309 nsGlobalWindow *w;
1310 while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) {
1311 PR_REMOVE_AND_INIT_LINK(w);
1314 DropOuterWindowDocs();
1315 } else {
1316 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1317 mMutationBits ? 1 : 0);
1319 if (mListenerManager) {
1320 mListenerManager->Disconnect();
1321 mListenerManager = nullptr;
1324 // An inner window is destroyed, pull it out of the outer window's
1325 // list if inner windows.
1327 PR_REMOVE_LINK(this);
1329 // If our outer window's inner window is this window, null out the
1330 // outer window's reference to this window that's being deleted.
1331 nsGlobalWindow *outer = GetOuterWindowInternal();
1332 if (outer) {
1333 outer->MaybeClearInnerWindow(this);
1337 // Outer windows are always supposed to call CleanUp before letting themselves
1338 // be destroyed. And while CleanUp generally seems to be intended to clean up
1339 // outers, we've historically called it for both. Changing this would probably
1340 // involve auditing all of the references that inners and outers can have, and
1341 // separating the handling into CleanUp() and FreeInnerObjects.
1342 if (IsInnerWindow()) {
1343 CleanUp();
1344 } else {
1345 MOZ_ASSERT(mCleanedUp);
1348 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1349 if (ac)
1350 ac->RemoveWindowAsListener(this);
1352 nsLayoutStatics::Release();
1355 void
1356 nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject)
1358 MOZ_ASSERT(IsInnerWindow());
1359 mEventTargetObjects.PutEntry(aObject);
1362 void
1363 nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
1365 MOZ_ASSERT(IsInnerWindow());
1366 mEventTargetObjects.RemoveEntry(aObject);
1369 // static
1370 void
1371 nsGlobalWindow::ShutDown()
1373 if (gDumpFile && gDumpFile != stdout) {
1374 fclose(gDumpFile);
1376 gDumpFile = nullptr;
1378 NS_IF_RELEASE(gEntropyCollector);
1380 delete sWindowsById;
1381 sWindowsById = nullptr;
1384 // static
1385 void
1386 nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow)
1388 if (aWindow->mCachedXBLPrototypeHandlers &&
1389 aWindow->mCachedXBLPrototypeHandlers->Count() > 0) {
1390 aWindow->mCachedXBLPrototypeHandlers->Clear();
1394 void
1395 nsGlobalWindow::MaybeForgiveSpamCount()
1397 if (IsOuterWindow() &&
1398 IsPopupSpamWindow())
1400 SetPopupSpamWindow(false);
1401 --gOpenPopupSpamCount;
1402 NS_ASSERTION(gOpenPopupSpamCount >= 0,
1403 "Unbalanced decrement of gOpenPopupSpamCount");
1407 void
1408 nsGlobalWindow::DropOuterWindowDocs()
1410 MOZ_ASSERT(IsOuterWindow());
1411 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
1412 mDoc = nullptr;
1413 mSuspendedDoc = nullptr;
1416 void
1417 nsGlobalWindow::CleanUp()
1419 // Guarantee idempotence.
1420 if (mCleanedUp)
1421 return;
1422 mCleanedUp = true;
1424 mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
1425 mEventTargetObjects.Clear();
1427 if (mObserver) {
1428 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1429 if (os) {
1430 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1431 os->RemoveObserver(mObserver, "dom-storage2-changed");
1434 #ifdef MOZ_B2G
1435 DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
1436 DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
1437 #endif // MOZ_B2G
1439 if (mIdleService) {
1440 mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
1443 Preferences::RemoveObserver(mObserver, "intl.accept_languages");
1445 // Drop its reference to this dying window, in case for some bogus reason
1446 // the object stays around.
1447 mObserver->Forget();
1448 NS_RELEASE(mObserver);
1451 if (mNavigator) {
1452 mNavigator->Invalidate();
1453 mNavigator = nullptr;
1456 mScreen = nullptr;
1457 mMenubar = nullptr;
1458 mToolbar = nullptr;
1459 mLocationbar = nullptr;
1460 mPersonalbar = nullptr;
1461 mStatusbar = nullptr;
1462 mScrollbars = nullptr;
1463 mLocation = nullptr;
1464 mHistory = nullptr;
1465 mFrames = nullptr;
1466 mWindowUtils = nullptr;
1467 mApplicationCache = nullptr;
1468 mIndexedDB = nullptr;
1470 mConsole = nullptr;
1472 mExternal = nullptr;
1474 mMozSelfSupport = nullptr;
1476 mPerformance = nullptr;
1478 #ifdef MOZ_WEBSPEECH
1479 mSpeechSynthesis = nullptr;
1480 #endif
1482 ClearControllers();
1484 mOpener = nullptr; // Forces Release
1485 if (mContext) {
1486 mContext = nullptr; // Forces Release
1488 mChromeEventHandler = nullptr; // Forces Release
1489 mParentTarget = nullptr;
1491 if (IsOuterWindow()) {
1492 nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
1493 if (inner) {
1494 inner->CleanUp();
1498 if (IsInnerWindow()) {
1499 DisableGamepadUpdates();
1500 mHasGamepad = false;
1501 } else {
1502 MOZ_ASSERT(!mHasGamepad);
1505 if (mCleanMessageManager) {
1506 NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned");
1507 nsGlobalChromeWindow *asChrome = static_cast<nsGlobalChromeWindow*>(this);
1508 if (asChrome->mMessageManager) {
1509 static_cast<nsFrameMessageManager*>(
1510 asChrome->mMessageManager.get())->Disconnect();
1514 mArguments = nullptr;
1515 mDialogArguments = nullptr;
1517 CleanupCachedXBLHandlers(this);
1519 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1520 mAudioContexts[i]->Shutdown();
1522 mAudioContexts.Clear();
1524 if (mIdleTimer) {
1525 mIdleTimer->Cancel();
1526 mIdleTimer = nullptr;
1529 DisableTimeChangeNotifications();
1532 void
1533 nsGlobalWindow::ClearControllers()
1535 if (mControllers) {
1536 uint32_t count;
1537 mControllers->GetControllerCount(&count);
1539 while (count--) {
1540 nsCOMPtr<nsIController> controller;
1541 mControllers->GetControllerAt(count, getter_AddRefs(controller));
1543 nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
1544 if (context)
1545 context->SetCommandContext(nullptr);
1548 mControllers = nullptr;
1552 void
1553 nsGlobalWindow::FreeInnerObjects()
1555 NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
1557 // Make sure that this is called before we null out the document and
1558 // other members that the window destroyed observers could
1559 // re-create.
1560 NotifyDOMWindowDestroyed(this);
1562 mInnerObjectsFreed = true;
1564 // Kill all of the workers for this window.
1565 mozilla::dom::workers::CancelWorkersForWindow(this);
1567 // Close all offline storages for this window.
1568 quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
1569 if (quotaManager) {
1570 quotaManager->AbortCloseStoragesForWindow(this);
1573 ClearAllTimeouts();
1575 if (mIdleTimer) {
1576 mIdleTimer->Cancel();
1577 mIdleTimer = nullptr;
1580 mIdleObservers.Clear();
1582 mChromeEventHandler = nullptr;
1584 if (mListenerManager) {
1585 mListenerManager->Disconnect();
1586 mListenerManager = nullptr;
1589 mLocation = nullptr;
1590 mHistory = nullptr;
1592 if (mNavigator) {
1593 mNavigator->OnNavigation();
1594 mNavigator->Invalidate();
1595 mNavigator = nullptr;
1598 if (mScreen) {
1599 mScreen = nullptr;
1602 if (mDoc) {
1603 // Remember the document's principal and URI.
1604 mDocumentPrincipal = mDoc->NodePrincipal();
1605 mDocumentURI = mDoc->GetDocumentURI();
1606 mDocBaseURI = mDoc->GetDocBaseURI();
1608 while (mDoc->EventHandlingSuppressed()) {
1609 mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false);
1612 // Note: we don't have to worry about eAnimationsOnly suppressions because
1613 // they won't leak.
1616 // Remove our reference to the document and the document principal.
1617 mFocusedNode = nullptr;
1619 if (mApplicationCache) {
1620 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect();
1621 mApplicationCache = nullptr;
1624 mIndexedDB = nullptr;
1626 NotifyWindowIDDestroyed("inner-window-destroyed");
1628 CleanupCachedXBLHandlers(this);
1630 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1631 mAudioContexts[i]->Shutdown();
1633 mAudioContexts.Clear();
1635 #ifdef MOZ_GAMEPAD
1636 DisableGamepadUpdates();
1637 mHasGamepad = false;
1638 mGamepads.Clear();
1639 #endif
1642 //*****************************************************************************
1643 // nsGlobalWindow::nsISupports
1644 //*****************************************************************************
1646 DOMCI_DATA(Window, nsGlobalWindow)
1648 // QueryInterface implementation for nsGlobalWindow
1649 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow)
1650 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1651 // Make sure this matches the cast in nsGlobalWindow::FromWrapper()
1652 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget)
1653 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1654 #ifdef MOZ_B2G
1655 NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G)
1656 #endif // MOZ_B2G
1657 #ifdef MOZ_WEBSPEECH
1658 NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter)
1659 #endif // MOZ_B2G
1660 NS_INTERFACE_MAP_ENTRY(nsIDOMJSWindow)
1661 if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) {
1662 foundInterface = static_cast<nsIDOMWindowInternal*>(this);
1663 if (!sWarnedAboutWindowInternal) {
1664 sWarnedAboutWindowInternal = true;
1665 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1666 NS_LITERAL_CSTRING("Extensions"), mDoc,
1667 nsContentUtils::eDOM_PROPERTIES,
1668 "nsIDOMWindowInternalWarning");
1670 } else
1671 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1672 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1673 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1674 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
1675 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1676 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow)
1677 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1678 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1679 NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance)
1680 NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver)
1681 NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers)
1682 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window)
1683 NS_INTERFACE_MAP_END
1686 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
1687 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
1689 static PLDHashOperator
1690 MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
1692 JS::ExposeObjectToActiveJS(aData);
1693 return PL_DHASH_NEXT;
1696 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
1697 if (tmp->IsBlackForCC(false)) {
1698 if (tmp->mCachedXBLPrototypeHandlers) {
1699 tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr);
1701 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1702 elm->MarkForCC();
1704 tmp->UnmarkGrayTimers();
1705 return true;
1707 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1709 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
1710 return tmp->IsBlackForCC(true);
1711 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1713 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow)
1714 return tmp->IsBlackForCC(false);
1715 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1717 inline void
1718 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
1719 IdleObserverHolder& aField,
1720 const char* aName,
1721 unsigned aFlags)
1723 CycleCollectionNoteChild(aCallback, aField.mIdleObserver.get(), aName, aFlags);
1726 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow)
1728 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
1729 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1730 char name[512];
1731 nsAutoCString uri;
1732 if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) {
1733 tmp->mDoc->GetDocumentURI()->GetSpec(uri);
1735 PR_snprintf(name, sizeof(name), "nsGlobalWindow #%llu %s %s",
1736 tmp->mWindowID, tmp->IsInnerWindow() ? "inner" : "outer",
1737 uri.get());
1738 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1739 } else {
1740 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get())
1743 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
1745 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1746 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
1747 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
1748 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
1749 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1751 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1753 #ifdef MOZ_WEBSPEECH
1754 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1755 #endif
1757 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1759 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1761 for (nsTimeout* timeout = tmp->mTimeouts.getFirst();
1762 timeout;
1763 timeout = timeout->getNext()) {
1764 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
1767 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1768 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1769 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
1770 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1771 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1772 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
1773 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
1774 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
1776 #ifdef MOZ_GAMEPAD
1777 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1778 #endif
1780 // Traverse stuff from nsPIDOMWindow
1781 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1782 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1783 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
1784 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode)
1786 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1787 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1788 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1789 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1790 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1791 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1792 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1793 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1794 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1795 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
1796 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1797 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1799 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
1800 nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
1802 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
1804 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1805 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
1806 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
1807 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
1808 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1810 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1812 #ifdef MOZ_WEBSPEECH
1813 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1814 #endif
1816 if (tmp->mOuterWindow) {
1817 static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp);
1818 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1821 if (tmp->mListenerManager) {
1822 tmp->mListenerManager->Disconnect();
1823 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1825 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1826 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1827 if (tmp->mApplicationCache) {
1828 static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect();
1829 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
1831 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1832 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1833 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
1834 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents)
1835 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
1837 #ifdef MOZ_GAMEPAD
1838 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1839 #endif
1841 // Unlink stuff from nsPIDOMWindow
1842 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1843 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1844 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
1845 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode)
1847 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1848 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1849 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1850 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1851 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1852 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1853 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1854 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1855 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1856 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
1857 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1858 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1860 #ifdef DEBUG
1861 void
1862 nsGlobalWindow::RiskyUnlink()
1864 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1866 #endif
1868 struct TraceData
1870 const TraceCallbacks& callbacks;
1871 void* closure;
1874 static PLDHashOperator
1875 TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
1877 TraceData* data = static_cast<TraceData*>(aClosure);
1878 data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure);
1879 return PL_DHASH_NEXT;
1882 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
1883 if (tmp->mCachedXBLPrototypeHandlers) {
1884 TraceData data = { aCallbacks, aClosure };
1885 tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data);
1887 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1888 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1890 bool
1891 nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
1893 if (!nsCCUncollectableMarker::sGeneration) {
1894 return false;
1897 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1898 IsBlack()) &&
1899 (!aTracingNeeded ||
1900 HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
1903 void
1904 nsGlobalWindow::UnmarkGrayTimers()
1906 for (nsTimeout* timeout = mTimeouts.getFirst();
1907 timeout;
1908 timeout = timeout->getNext()) {
1909 if (timeout->mScriptHandler) {
1910 Function* f = timeout->mScriptHandler->GetCallback();
1911 if (f) {
1912 // Callable() already does xpc_UnmarkGrayObject.
1913 DebugOnly<JS::Handle<JSObject*> > o = f->Callable();
1914 MOZ_ASSERT(!xpc_IsGrayGCThing(o.value), "Should have been unmarked");
1920 //*****************************************************************************
1921 // nsGlobalWindow::nsIScriptGlobalObject
1922 //*****************************************************************************
1924 nsresult
1925 nsGlobalWindow::EnsureScriptEnvironment()
1927 nsGlobalWindow* outer = GetOuterWindowInternal();
1928 if (!outer) {
1929 NS_WARNING("No outer window available!");
1930 return NS_ERROR_FAILURE;
1933 if (outer->GetWrapperPreserveColor()) {
1934 return NS_OK;
1937 NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(),
1938 "No cached wrapper, but we have an inner window?");
1940 // If this window is a [i]frame, don't bother GC'ing when the frame's context
1941 // is destroyed since a GC will happen when the frameset or host document is
1942 // destroyed anyway.
1943 nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer);
1945 NS_ASSERTION(!outer->mContext, "Will overwrite mContext!");
1947 // should probably assert the context is clean???
1948 context->WillInitializeContext();
1950 nsresult rv = context->InitContext();
1951 NS_ENSURE_SUCCESS(rv, rv);
1953 outer->mContext = context;
1954 return NS_OK;
1957 nsIScriptContext *
1958 nsGlobalWindow::GetScriptContext()
1960 nsGlobalWindow* outer = GetOuterWindowInternal();
1961 return outer ? outer->mContext : nullptr;
1964 JSObject *
1965 nsGlobalWindow::GetGlobalJSObject()
1967 return FastGetGlobalJSObject();
1970 void
1971 nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
1973 TraceWrapper(aTrc, "active window global");
1976 /* static */
1977 JSObject*
1978 nsGlobalWindow::OuterObject(JSContext* aCx, JS::Handle<JSObject*> aObj)
1980 nsGlobalWindow* origWin = UnwrapDOMObject<nsGlobalWindow>(aObj);
1981 nsGlobalWindow* win = origWin->GetOuterWindowInternal();
1983 if (!win) {
1984 // If we no longer have an outer window. No code should ever be
1985 // running on a window w/o an outer, which means this hook should
1986 // never be called when we have no outer. But just in case, return
1987 // null to prevent leaking an inner window to code in a different
1988 // window.
1989 NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
1990 Throw(aCx, NS_ERROR_UNEXPECTED);
1991 return nullptr;
1994 JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject());
1995 MOZ_ASSERT(winObj);
1997 // Note that while |wrapper| is same-compartment with cx, the outer window
1998 // might not be. If we're running script in an inactive scope and evalute
1999 // |this|, the outer window is actually a cross-compartment wrapper. So we
2000 // need to wrap here.
2001 if (!JS_WrapObject(aCx, &winObj)) {
2002 NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
2003 Throw(aCx, NS_ERROR_UNEXPECTED);
2004 return nullptr;
2007 return winObj;
2010 bool
2011 nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument)
2013 MOZ_ASSERT(IsOuterWindow());
2015 // We reuse the inner window when:
2016 // a. We are currently at our original document.
2017 // b. At least one of the following conditions are true:
2018 // -- The new document is the same as the old document. This means that we're
2019 // getting called from document.open().
2020 // -- The new document has the same origin as what we have loaded right now.
2022 if (!mDoc || !aNewDocument) {
2023 return false;
2026 if (!mDoc->IsInitialDocument()) {
2027 return false;
2030 NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()),
2031 "How'd this happen?");
2033 // Great, we're the original document, check for one of the other
2034 // conditions.
2036 if (mDoc == aNewDocument) {
2037 return true;
2040 bool equal;
2041 if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(),
2042 &equal)) &&
2043 equal) {
2044 // The origin is the same.
2045 return true;
2048 return false;
2051 void
2052 nsGlobalWindow::SetInitialPrincipalToSubject()
2054 MOZ_ASSERT(IsOuterWindow());
2056 // First, grab the subject principal.
2057 nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::SubjectPrincipal();
2059 // Now, if we're about to use the system principal or an nsExpandedPrincipal,
2060 // make sure we're not using it for a content docshell.
2061 if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) &&
2062 GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) {
2063 newWindowPrincipal = nullptr;
2066 // If there's an existing document, bail if it either:
2067 if (mDoc) {
2068 // (a) is not an initial about:blank document, or
2069 if (!mDoc->IsInitialDocument())
2070 return;
2071 // (b) already has the correct principal.
2072 if (mDoc->NodePrincipal() == newWindowPrincipal)
2073 return;
2075 #ifdef DEBUG
2076 // If we have a document loaded at this point, it had better be about:blank.
2077 // Otherwise, something is really weird.
2078 nsCOMPtr<nsIURI> uri;
2079 mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri));
2080 NS_ASSERTION(uri && NS_IsAboutBlank(uri) &&
2081 NS_IsAboutBlank(mDoc->GetDocumentURI()),
2082 "Unexpected original document");
2083 #endif
2086 GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal);
2087 mDoc->SetIsInitialDocument(true);
2089 nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell();
2091 if (shell && !shell->DidInitialize()) {
2092 // Ensure that if someone plays with this document they will get
2093 // layout happening.
2094 nsRect r = shell->GetPresContext()->GetVisibleArea();
2095 shell->Initialize(r.width, r.height);
2099 PopupControlState
2100 PushPopupControlState(PopupControlState aState, bool aForce)
2102 MOZ_ASSERT(NS_IsMainThread());
2104 PopupControlState oldState = gPopupControlState;
2106 if (aState < gPopupControlState || aForce) {
2107 gPopupControlState = aState;
2110 return oldState;
2113 void
2114 PopPopupControlState(PopupControlState aState)
2116 MOZ_ASSERT(NS_IsMainThread());
2118 gPopupControlState = aState;
2121 PopupControlState
2122 nsGlobalWindow::PushPopupControlState(PopupControlState aState,
2123 bool aForce) const
2125 return ::PushPopupControlState(aState, aForce);
2128 void
2129 nsGlobalWindow::PopPopupControlState(PopupControlState aState) const
2131 ::PopPopupControlState(aState);
2134 PopupControlState
2135 nsGlobalWindow::GetPopupControlState() const
2137 MOZ_ASSERT(NS_IsMainThread());
2138 return gPopupControlState;
2141 #define WINDOWSTATEHOLDER_IID \
2142 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
2144 class WindowStateHolder MOZ_FINAL : public nsISupports
2146 public:
2147 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
2148 NS_DECL_ISUPPORTS
2150 WindowStateHolder(nsIScriptContext* aContext, nsGlobalWindow *aWindow);
2152 nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
2154 void DidRestoreWindow()
2156 mInnerWindow = nullptr;
2157 mInnerWindowReflector = nullptr;
2160 protected:
2161 ~WindowStateHolder();
2163 nsGlobalWindow *mInnerWindow;
2164 // We hold onto this to make sure the inner window doesn't go away. The outer
2165 // window ends up recalculating it anyway.
2166 JS::PersistentRooted<JSObject*> mInnerWindowReflector;
2169 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
2171 WindowStateHolder::WindowStateHolder(nsIScriptContext* aContext,
2172 nsGlobalWindow* aWindow)
2173 : mInnerWindow(aWindow),
2174 mInnerWindowReflector(aContext->GetNativeContext(), aWindow->GetWrapper())
2176 NS_PRECONDITION(aWindow, "null window");
2177 NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
2179 aWindow->SuspendTimeouts();
2181 // When a global goes into the bfcache, we disable script.
2182 xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false);
2185 WindowStateHolder::~WindowStateHolder()
2187 if (mInnerWindow) {
2188 // This window was left in the bfcache and is now going away. We need to
2189 // free it up.
2190 // Note that FreeInnerObjects may already have been called on the
2191 // inner window if its outer has already had SetDocShell(null)
2192 // called.
2193 mInnerWindow->FreeInnerObjects();
2197 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
2199 // We need certain special behavior for remote XUL whitelisted domains, but we
2200 // don't want that behavior to take effect in automation, because we whitelist
2201 // all the mochitest domains. So we need to check a pref here.
2202 static bool
2203 TreatAsRemoteXUL(nsIPrincipal* aPrincipal)
2205 MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(aPrincipal));
2206 return nsContentUtils::AllowXULXBLForPrincipal(aPrincipal) &&
2207 !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
2210 static bool
2211 EnablePrivilege(JSContext* cx, unsigned argc, JS::Value* vp)
2213 Telemetry::Accumulate(Telemetry::ENABLE_PRIVILEGE_EVER_CALLED, true);
2214 return xpc::EnableUniversalXPConnect(cx);
2217 static const JSFunctionSpec EnablePrivilegeSpec[] = {
2218 JS_FS("enablePrivilege", EnablePrivilege, 1, 0),
2219 JS_FS_END
2222 static bool
2223 InitializeLegacyNetscapeObject(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
2225 JSAutoCompartment ac(aCx, aGlobal);
2227 // Note: MathJax depends on window.netscape being exposed. See bug 791526.
2228 JS::Rooted<JSObject*> obj(aCx);
2229 obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr);
2230 NS_ENSURE_TRUE(obj, false);
2232 obj = JS_DefineObject(aCx, obj, "security", nullptr);
2233 NS_ENSURE_TRUE(obj, false);
2235 // We hide enablePrivilege behind a pref because it has been altered in a
2236 // way that makes it fundamentally insecure to use in production. Mozilla
2237 // uses this pref during automated testing to support legacy test code that
2238 // uses enablePrivilege. If you're not doing test automation, you _must_ not
2239 // flip this pref, or you will be exposing all your users to security
2240 // vulnerabilities.
2241 if (!Preferences::GetBool("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer")) {
2242 return true;
2245 /* Define PrivilegeManager object with the necessary "static" methods. */
2246 obj = JS_DefineObject(aCx, obj, "PrivilegeManager", nullptr);
2247 NS_ENSURE_TRUE(obj, false);
2249 return JS_DefineFunctions(aCx, obj, EnablePrivilegeSpec);
2253 * Create a new global object that will be used for an inner window.
2254 * Return the native global and an nsISupports 'holder' that can be used
2255 * to manage the lifetime of it.
2257 static nsresult
2258 CreateNativeGlobalForInner(JSContext* aCx,
2259 nsGlobalWindow* aNewInner,
2260 nsIURI* aURI,
2261 nsIPrincipal* aPrincipal,
2262 JS::MutableHandle<JSObject*> aGlobal)
2264 MOZ_ASSERT(aCx);
2265 MOZ_ASSERT(aNewInner);
2266 MOZ_ASSERT(aNewInner->IsInnerWindow());
2267 MOZ_ASSERT(aPrincipal);
2269 // DOMWindow with nsEP is not supported, we have to make sure
2270 // no one creates one accidentally.
2271 nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal);
2272 MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
2274 nsGlobalWindow *top = nullptr;
2275 if (aNewInner->GetOuterWindow()) {
2276 top = aNewInner->GetTop();
2278 JS::CompartmentOptions options;
2279 if (top) {
2280 if (top->GetGlobalJSObject()) {
2281 options.setSameZoneAs(top->GetGlobalJSObject());
2285 // Determine if we need the Components object.
2286 bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
2287 TreatAsRemoteXUL(aPrincipal);
2288 uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
2289 flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
2291 if (aNewInner->IsDOMBinding()) {
2292 aGlobal.set(WindowBinding::Wrap(aCx, aNewInner, aNewInner, options,
2293 nsJSPrincipals::get(aPrincipal), false));
2294 if (!aGlobal || !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
2295 return NS_ERROR_FAILURE;
2297 } else {
2298 nsIXPConnect* xpc = nsContentUtils::XPConnect();
2299 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
2300 nsresult rv = xpc->InitClassesWithNewWrappedGlobal(
2301 aCx, ToSupports(aNewInner),
2302 aPrincipal, flags, options, getter_AddRefs(holder));
2303 NS_ENSURE_SUCCESS(rv, rv);
2305 aGlobal.set(holder->GetJSObject());
2306 MOZ_ASSERT(aGlobal);
2309 MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
2311 // Set the location information for the new global, so that tools like
2312 // about:memory may use that information
2313 xpc::SetLocationForGlobal(aGlobal, aURI);
2315 if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) {
2316 return NS_ERROR_FAILURE;
2319 return NS_OK;
2322 nsresult
2323 nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
2324 nsISupports* aState,
2325 bool aForceReuseInnerWindow)
2327 NS_PRECONDITION(mDocumentPrincipal == nullptr,
2328 "mDocumentPrincipal prematurely set!");
2329 MOZ_ASSERT(aDocument);
2331 if (IsInnerWindow()) {
2332 if (!mOuterWindow) {
2333 return NS_ERROR_NOT_INITIALIZED;
2336 // Refuse to set a new document if the call came from an inner
2337 // window that's not the current inner window.
2338 if (mOuterWindow->GetCurrentInnerWindow() != this) {
2339 return NS_ERROR_NOT_AVAILABLE;
2342 return GetOuterWindowInternal()->SetNewDocument(aDocument, aState,
2343 aForceReuseInnerWindow);
2346 NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows");
2348 // Bail out early if we're in process of closing down the window.
2349 NS_ENSURE_STATE(!mCleanedUp);
2351 if (IsFrozen()) {
2352 // This outer is now getting its first inner, thaw the outer now
2353 // that it's ready and is getting an inner window.
2355 Thaw();
2358 NS_ASSERTION(!GetCurrentInnerWindow() ||
2359 GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
2360 "Uh, mDoc doesn't match the current inner window "
2361 "document!");
2363 bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
2364 if (aForceReuseInnerWindow &&
2365 !wouldReuseInnerWindow &&
2366 mDoc &&
2367 mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
2368 NS_ERROR("Attempted forced inner window reuse while changing principal");
2369 return NS_ERROR_UNEXPECTED;
2372 nsCOMPtr<nsIDocument> oldDoc = mDoc;
2374 #ifndef MOZ_DISABLE_CRYPTOLEGACY
2375 // clear smartcard events, our document has gone away.
2376 if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) {
2377 nsresult rv = mCrypto->SetEnableSmartCardEvents(false);
2378 NS_ENSURE_SUCCESS(rv, rv);
2380 #endif
2382 AutoJSAPI jsapi;
2383 jsapi.Init();
2384 JSContext *cx = jsapi.cx();
2386 if (!mDoc) {
2387 // First document load.
2389 // Get our private root. If it is equal to us, then we need to
2390 // attach our global key bindings that handles browser scrolling
2391 // and other browser commands.
2392 nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot();
2394 if (privateRoot == static_cast<nsIDOMWindow*>(this)) {
2395 nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler);
2399 /* No mDocShell means we're already been partially closed down. When that
2400 happens, setting status isn't a big requirement, so don't. (Doesn't happen
2401 under normal circumstances, but bug 49615 describes a case.) */
2403 nsContentUtils::AddScriptRunner(
2404 NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus));
2406 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
2407 // window (see bug 776497). Be safe.
2408 bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
2409 GetCurrentInnerWindowInternal();
2411 nsresult rv = NS_OK;
2413 // Set mDoc even if this is an outer window to avoid
2414 // having to *always* reach into the inner window to find the
2415 // document.
2416 mDoc = aDocument;
2417 if (IsInnerWindow() && IsDOMBinding()) {
2418 WindowBinding::ClearCachedDocumentValue(cx, this);
2421 // Take this opportunity to clear mSuspendedDoc. Our old inner window is now
2422 // responsible for unsuspending it.
2423 mSuspendedDoc = nullptr;
2425 #ifdef DEBUG
2426 mLastOpenedURI = aDocument->GetDocumentURI();
2427 #endif
2429 mContext->WillInitializeContext();
2431 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
2433 if (currentInner && currentInner->mNavigator) {
2434 currentInner->mNavigator->OnNavigation();
2437 nsRefPtr<nsGlobalWindow> newInnerWindow;
2438 bool createdInnerWindow = false;
2440 bool thisChrome = IsChromeWindow();
2442 // Check if we're anywhere near the stack limit before we reach the
2443 // transplanting code, since it has no good way to handle errors. This uses
2444 // the untrusted script limit, which is not strictly necessary since no
2445 // actual script should run.
2446 JS_CHECK_RECURSION_CONSERVATIVE(cx, return NS_ERROR_FAILURE);
2448 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
2449 NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
2451 JS::Rooted<JSObject*> newInnerGlobal(cx);
2452 if (reUseInnerWindow) {
2453 // We're reusing the current inner window.
2454 NS_ASSERTION(!currentInner->IsFrozen(),
2455 "We should never be reusing a shared inner window");
2456 newInnerWindow = currentInner;
2457 newInnerGlobal = currentInner->GetWrapperPreserveColor();
2459 if (aDocument != oldDoc) {
2460 JS::ExposeObjectToActiveJS(newInnerGlobal);
2463 // We're reusing the inner window, but this still counts as a navigation,
2464 // so all expandos and such defined on the outer window should go away. Force
2465 // all Xray wrappers to be recomputed.
2466 JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor());
2467 JS::ExposeObjectToActiveJS(rootedObject);
2468 if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
2469 return NS_ERROR_FAILURE;
2472 // Inner windows are only reused for same-origin principals, but the principals
2473 // don't necessarily match exactly. Update the principal on the compartment to
2474 // match the new document.
2475 // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here
2476 // because we haven't yet set its mDoc to aDocument.
2477 JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal);
2478 #ifdef DEBUG
2479 bool sameOrigin = false;
2480 nsIPrincipal *existing =
2481 nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
2482 aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
2483 MOZ_ASSERT(sameOrigin);
2484 #endif
2485 JS_SetCompartmentPrincipals(compartment,
2486 nsJSPrincipals::get(aDocument->NodePrincipal()));
2487 } else {
2488 if (aState) {
2489 newInnerWindow = wsh->GetInnerWindow();
2490 newInnerGlobal = newInnerWindow->GetWrapperPreserveColor();
2491 } else {
2492 if (thisChrome) {
2493 newInnerWindow = new nsGlobalChromeWindow(this);
2494 } else if (mIsModalContentWindow) {
2495 newInnerWindow = new nsGlobalModalWindow(this);
2496 } else {
2497 newInnerWindow = new nsGlobalWindow(this);
2500 // Freeze the outer window and null out the inner window so
2501 // that initializing classes on the new inner doesn't end up
2502 // reaching into the old inner window for classes etc.
2504 // [This happens with Object.prototype when XPConnect creates
2505 // a temporary global while initializing classes; the reason
2506 // being that xpconnect creates the temp global w/o a parent
2507 // and proto, which makes the JS engine look up classes in
2508 // cx->globalObject, i.e. this outer window].
2510 mInnerWindow = nullptr;
2512 Freeze();
2513 mCreatingInnerWindow = true;
2514 // Every script context we are initialized with must create a
2515 // new global.
2516 rv = CreateNativeGlobalForInner(cx, newInnerWindow,
2517 aDocument->GetDocumentURI(),
2518 aDocument->NodePrincipal(),
2519 &newInnerGlobal);
2520 NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal &&
2521 newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
2522 "Failed to get script global");
2524 mCreatingInnerWindow = false;
2525 createdInnerWindow = true;
2526 Thaw();
2528 NS_ENSURE_SUCCESS(rv, rv);
2531 if (currentInner && currentInner->GetWrapperPreserveColor()) {
2532 if (oldDoc == aDocument) {
2533 // Move the navigator from the old inner window to the new one since
2534 // this is a document.write. This is safe from a same-origin point of
2535 // view because document.write can only be used by the same origin.
2536 newInnerWindow->mNavigator = currentInner->mNavigator;
2537 currentInner->mNavigator = nullptr;
2538 if (newInnerWindow->mNavigator) {
2539 newInnerWindow->mNavigator->SetWindow(newInnerWindow);
2542 // Make a copy of the old window's performance object on document.open.
2543 // Note that we have to force eager creation of it here, because we need
2544 // to grab the current document channel and whatnot before that changes.
2545 currentInner->CreatePerformanceObjectIfNeeded();
2546 if (currentInner->mPerformance) {
2547 newInnerWindow->mPerformance =
2548 new nsPerformance(newInnerWindow,
2549 currentInner->mPerformance->GetDOMTiming(),
2550 currentInner->mPerformance->GetChannel(),
2551 currentInner->mPerformance->GetParentPerformance());
2555 // Don't free objects on our current inner window if it's going to be
2556 // held in the bfcache.
2557 if (!currentInner->IsFrozen()) {
2558 currentInner->FreeInnerObjects();
2562 mInnerWindow = newInnerWindow;
2564 if (!GetWrapperPreserveColor()) {
2565 JS::Rooted<JSObject*> outer(cx,
2566 NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2567 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
2569 js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
2571 // Inform the nsJSContext, which is the canonical holder of the outer.
2572 mContext->SetWindowProxy(outer);
2573 mContext->DidInitializeContext();
2575 SetWrapper(mContext->GetWindowProxy());
2576 } else {
2577 JS::ExposeObjectToActiveJS(newInnerGlobal);
2578 JS::Rooted<JSObject*> outerObject(cx,
2579 NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2580 if (!outerObject) {
2581 NS_ERROR("out of memory");
2582 return NS_ERROR_FAILURE;
2585 JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
2587 js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
2589 outerObject = xpc::TransplantObject(cx, obj, outerObject);
2590 if (!outerObject) {
2591 NS_ERROR("unable to transplant wrappers, probably OOM");
2592 return NS_ERROR_FAILURE;
2595 js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
2597 SetWrapper(outerObject);
2600 JSAutoCompartment ac(cx, outerObject);
2602 JS_SetParent(cx, outerObject, newInnerGlobal);
2604 // Inform the nsJSContext, which is the canonical holder of the outer.
2605 mContext->SetWindowProxy(outerObject);
2607 NS_ASSERTION(!JS_IsExceptionPending(cx),
2608 "We might overwrite a pending exception!");
2609 XPCWrappedNativeScope* scope = xpc::ObjectScope(outerObject);
2610 if (scope->mWaiverWrapperMap) {
2611 scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal);
2616 // Enter the new global's compartment.
2617 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
2619 // Set scriptability based on the state of the docshell.
2620 bool allow = GetDocShell()->GetCanExecuteScripts();
2621 xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
2623 // If we created a new inner window above, we need to do the last little bit
2624 // of initialization now that the dust has settled.
2625 if (createdInnerWindow) {
2626 if (newInnerWindow->IsDOMBinding()) {
2627 JS::Rooted<JSObject*> global(cx, newInnerGlobal);
2628 JS::Rooted<JSObject*> proto(cx);
2629 JS_GetPrototype(cx, global, &proto);
2630 WindowNamedPropertiesHandler::Install(cx, proto);
2631 } else {
2632 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2633 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
2634 nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal,
2635 getter_AddRefs(wrapper));
2636 NS_ENSURE_SUCCESS(rv, rv);
2637 NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
2638 rv = wrapper->FinishInitForWrappedGlobal();
2639 NS_ENSURE_SUCCESS(rv, rv);
2643 if (!aState) {
2644 JS::Rooted<JSObject*> rootedWrapper(cx, GetWrapperPreserveColor());
2645 if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper,
2646 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
2647 JS_PropertyStub, JS_StrictPropertyStub)) {
2648 NS_ERROR("can't create the 'window' property");
2649 return NS_ERROR_FAILURE;
2654 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
2656 if (!aState && !reUseInnerWindow) {
2657 // Loading a new page and creating a new inner window, *not*
2658 // restoring from session history.
2660 // Now that both the the inner and outer windows are initialized
2661 // let the script context do its magic to hook them together.
2662 MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
2663 #ifdef DEBUG
2664 JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
2665 JS::Rooted<JSObject*> proto1(cx), proto2(cx);
2666 JS_GetPrototype(cx, rootedJSObject, &proto1);
2667 JS_GetPrototype(cx, newInnerGlobal, &proto2);
2668 NS_ASSERTION(proto1 == proto2,
2669 "outer and inner globals should have the same prototype");
2670 #endif
2672 nsCOMPtr<Element> frame = GetFrameElementInternal();
2673 if (frame) {
2674 nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow();
2675 if (parentWindow && parentWindow->TimeoutSuspendCount()) {
2676 SuspendTimeouts(parentWindow->TimeoutSuspendCount());
2681 // Add an extra ref in case we release mContext during GC.
2682 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
2684 aDocument->SetScriptGlobalObject(newInnerWindow);
2686 if (!aState) {
2687 if (reUseInnerWindow) {
2689 if (newInnerWindow->mDoc != aDocument) {
2690 newInnerWindow->mDoc = aDocument;
2692 // The storage objects contain the URL of the window. We have to
2693 // recreate them when the innerWindow is reused.
2694 newInnerWindow->mLocalStorage = nullptr;
2695 newInnerWindow->mSessionStorage = nullptr;
2697 if (newInnerWindow->IsDOMBinding()) {
2698 WindowBinding::ClearCachedDocumentValue(cx, newInnerWindow);
2699 } else {
2700 // We're reusing the inner window for a new document. In this
2701 // case we don't clear the inner window's scope, but we must
2702 // make sure the cached document property gets updated.
2704 JS::Rooted<JSObject*> obj(cx,
2705 currentInner->GetWrapperPreserveColor());
2706 ::JS_DeleteProperty(cx, obj, "document");
2709 } else {
2710 newInnerWindow->InnerSetNewDocument(cx, aDocument);
2712 // Initialize DOM classes etc on the inner window.
2713 JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
2714 rv = mContext->InitClasses(obj);
2715 NS_ENSURE_SUCCESS(rv, rv);
2718 // If the document comes from a JAR, check if the channel was determined
2719 // to be unsafe. If so, permanently disable script on the compartment by
2720 // calling Block() and throwing away the key.
2721 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
2722 if (jarChannel && jarChannel->GetIsUnsafe()) {
2723 xpc::Scriptability::Get(newInnerGlobal).Block();
2726 if (mArguments) {
2727 newInnerWindow->DefineArgumentsProperty(mArguments);
2728 mArguments = nullptr;
2731 // Give the new inner window our chrome event handler (since it
2732 // doesn't have one).
2733 newInnerWindow->mChromeEventHandler = mChromeEventHandler;
2736 mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
2737 mContext->DidInitializeContext();
2739 // We wait to fire the debugger hook until the window is all set up and hooked
2740 // up with the outer. See bug 969156.
2741 if (createdInnerWindow) {
2742 // AutoEntryScript required to invoke debugger hook, which is a
2743 // Gecko-specific concept at present.
2744 AutoEntryScript aes(newInnerWindow);
2745 JS::Rooted<JSObject*> global(aes.cx(), newInnerWindow->GetWrapper());
2746 JS_FireOnNewGlobalObject(aes.cx(), global);
2749 if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
2750 // We should probably notify. However if this is the, arguably bad,
2751 // situation when we're creating a temporary non-chrome-about-blank
2752 // document in a chrome docshell, don't notify just yet. Instead wait
2753 // until we have a real chrome doc.
2754 if (!mDocShell ||
2755 mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome ||
2756 nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) {
2757 newInnerWindow->mHasNotifiedGlobalCreated = true;
2758 nsContentUtils::AddScriptRunner(
2759 NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated));
2763 PreloadLocalStorage();
2765 return NS_OK;
2768 void
2769 nsGlobalWindow::PreloadLocalStorage()
2771 MOZ_ASSERT(IsOuterWindow());
2773 if (!Preferences::GetBool(kStorageEnabled)) {
2774 return;
2777 if (IsChromeWindow()) {
2778 return;
2781 nsIPrincipal* principal = GetPrincipal();
2782 if (!principal) {
2783 return;
2786 nsresult rv;
2788 nsCOMPtr<nsIDOMStorageManager> storageManager =
2789 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
2790 if (NS_FAILED(rv)) {
2791 return;
2794 storageManager->PrecacheStorage(principal);
2797 void
2798 nsGlobalWindow::DispatchDOMWindowCreated()
2800 MOZ_ASSERT(IsOuterWindow());
2802 if (!mDoc) {
2803 return;
2806 // Fire DOMWindowCreated at chrome event listeners
2807 nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"),
2808 true /* bubbles */,
2809 false /* not cancellable */);
2811 nsCOMPtr<nsIObserverService> observerService =
2812 mozilla::services::GetObserverService();
2813 if (observerService) {
2814 nsAutoString origin;
2815 nsIPrincipal* principal = mDoc->NodePrincipal();
2816 nsContentUtils::GetUTFOrigin(principal, origin);
2817 observerService->
2818 NotifyObservers(static_cast<nsIDOMWindow*>(this),
2819 nsContentUtils::IsSystemPrincipal(principal) ?
2820 "chrome-document-global-created" :
2821 "content-document-global-created",
2822 origin.get());
2826 void
2827 nsGlobalWindow::ClearStatus()
2829 SetStatus(EmptyString());
2832 void
2833 nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument)
2835 NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows");
2836 MOZ_ASSERT(aDocument);
2838 #ifdef PR_LOGGING
2839 if (gDOMLeakPRLog && PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
2840 nsIURI *uri = aDocument->GetDocumentURI();
2841 nsAutoCString spec;
2842 if (uri)
2843 uri->GetSpec(spec);
2844 PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get());
2846 #endif
2848 mDoc = aDocument;
2849 if (IsDOMBinding()) {
2850 WindowBinding::ClearCachedDocumentValue(aCx, this);
2852 mFocusedNode = nullptr;
2853 mLocalStorage = nullptr;
2854 mSessionStorage = nullptr;
2856 #ifdef DEBUG
2857 mLastOpenedURI = aDocument->GetDocumentURI();
2858 #endif
2860 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
2861 mMutationBits ? 1 : 0);
2863 // Clear our mutation bitfield.
2864 mMutationBits = 0;
2867 void
2868 nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
2870 NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!");
2871 MOZ_ASSERT(aDocShell);
2873 if (aDocShell == mDocShell) {
2874 return;
2877 mDocShell = aDocShell; // Weak Reference
2879 NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
2881 if (mFrames) {
2882 mFrames->SetDocShell(aDocShell);
2885 // Get our enclosing chrome shell and retrieve its global window impl, so
2886 // that we can do some forwarding to the chrome document.
2887 nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
2888 mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2889 mChromeEventHandler = do_QueryInterface(chromeEventHandler);
2890 if (!mChromeEventHandler) {
2891 // We have no chrome event handler. If we have a parent,
2892 // get our chrome event handler from the parent. If
2893 // we don't have a parent, then we need to make a new
2894 // window root object that will function as a chrome event
2895 // handler and receive all events that occur anywhere inside
2896 // our window.
2897 nsCOMPtr<nsIDOMWindow> parentWindow;
2898 GetParent(getter_AddRefs(parentWindow));
2899 if (parentWindow.get() != static_cast<nsIDOMWindow*>(this)) {
2900 nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(parentWindow));
2901 mChromeEventHandler = piWindow->GetChromeEventHandler();
2903 else {
2904 mChromeEventHandler = NS_NewWindowRoot(this);
2908 bool docShellActive;
2909 mDocShell->GetIsActive(&docShellActive);
2910 mIsBackground = !docShellActive;
2913 void
2914 nsGlobalWindow::DetachFromDocShell()
2916 NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");
2918 // DetachFromDocShell means the window is being torn down. Drop our
2919 // reference to the script context, allowing it to be deleted
2920 // later. Meanwhile, keep our weak reference to the script object
2921 // so that it can be retrieved later (until it is finalized by the JS GC).
2923 NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!");
2925 // Call FreeInnerObjects on all inner windows, not just the current
2926 // one, since some could be held by WindowStateHolder objects that
2927 // are GC-owned.
2928 for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
2929 inner != this;
2930 inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
2931 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
2932 "bad outer window pointer");
2933 inner->FreeInnerObjects();
2936 // Make sure that this is called before we null out the document.
2937 NotifyDOMWindowDestroyed(this);
2939 NotifyWindowIDDestroyed("outer-window-destroyed");
2941 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
2943 if (currentInner) {
2944 NS_ASSERTION(mDoc, "Must have doc!");
2946 // Remember the document's principal and URI.
2947 mDocumentPrincipal = mDoc->NodePrincipal();
2948 mDocumentURI = mDoc->GetDocumentURI();
2949 mDocBaseURI = mDoc->GetDocBaseURI();
2951 // Release our document reference
2952 DropOuterWindowDocs();
2953 mFocusedNode = nullptr;
2956 ClearControllers();
2958 mChromeEventHandler = nullptr; // force release now
2960 if (mContext) {
2961 mContext->GC(JS::gcreason::SET_DOC_SHELL);
2962 mContext = nullptr;
2965 mDocShell = nullptr; // Weak Reference
2967 NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
2969 if (mFrames) {
2970 mFrames->SetDocShell(nullptr);
2973 MaybeForgiveSpamCount();
2974 CleanUp();
2977 void
2978 nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener,
2979 bool aOriginalOpener)
2981 FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));
2983 NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled,
2984 "aOriginalOpener is true, but not first call to "
2985 "SetOpenerWindow!");
2986 NS_ASSERTION(aOpener || !aOriginalOpener,
2987 "Shouldn't set mHadOriginalOpener if aOpener is null");
2989 #ifdef DEBUG
2990 nsCOMPtr<nsPIDOMWindow> opener = do_QueryInterface(aOpener);
2991 MOZ_ASSERT(!opener || opener->IsOuterWindow());
2992 #endif
2993 mOpener = do_GetWeakReference(aOpener);
2994 NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
2996 if (aOriginalOpener) {
2997 mHadOriginalOpener = true;
3000 #ifdef DEBUG
3001 mSetOpenerWindowCalled = true;
3002 #endif
3005 static
3006 already_AddRefed<EventTarget>
3007 TryGetTabChildGlobalAsEventTarget(nsISupports *aFrom)
3009 nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(aFrom);
3010 if (!frameLoaderOwner) {
3011 return nullptr;
3014 nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
3015 if (!frameLoader) {
3016 return nullptr;
3019 nsCOMPtr<EventTarget> target = frameLoader->GetTabChildGlobalAsEventTarget();
3020 return target.forget();
3023 void
3024 nsGlobalWindow::UpdateParentTarget()
3026 // Try to get our frame element's tab child global (its in-process message
3027 // manager). If that fails, fall back to the chrome event handler's tab
3028 // child global, and if it doesn't have one, just use the chrome event
3029 // handler itself.
3031 nsCOMPtr<Element> frameElement = GetFrameElementInternal();
3032 nsCOMPtr<EventTarget> eventTarget =
3033 TryGetTabChildGlobalAsEventTarget(frameElement);
3035 if (!eventTarget) {
3036 nsGlobalWindow* topWin = GetScriptableTop();
3037 if (topWin) {
3038 frameElement = topWin->GetFrameElementInternal();
3039 eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement);
3043 if (!eventTarget) {
3044 eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler);
3047 if (!eventTarget) {
3048 eventTarget = mChromeEventHandler;
3051 mParentTarget = eventTarget;
3054 EventTarget*
3055 nsGlobalWindow::GetTargetForDOMEvent()
3057 return GetOuterWindowInternal();
3060 EventTarget*
3061 nsGlobalWindow::GetTargetForEventTargetChain()
3063 return IsInnerWindow() ? this : GetCurrentInnerWindowInternal();
3066 nsresult
3067 nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor)
3069 return NS_OK;
3072 JSContext*
3073 nsGlobalWindow::GetJSContextForEventHandlers()
3075 return nullptr;
3078 nsresult
3079 nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor)
3081 NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?");
3082 static uint32_t count = 0;
3083 uint32_t msg = aVisitor.mEvent->message;
3085 aVisitor.mCanHandle = true;
3086 aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119
3087 if ((msg == NS_MOUSE_MOVE) && gEntropyCollector) {
3088 //Chances are this counter will overflow during the life of the
3089 //process, but that's OK for our case. Means we get a little
3090 //more entropy.
3091 if (count++ % 100 == 0) {
3092 //Since the high bits seem to be zero's most of the time,
3093 //let's only take the lowest half of the point structure.
3094 int16_t myCoord[2];
3096 myCoord[0] = aVisitor.mEvent->refPoint.x;
3097 myCoord[1] = aVisitor.mEvent->refPoint.y;
3098 gEntropyCollector->RandomUpdate((void*)myCoord, sizeof(myCoord));
3099 gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time),
3100 sizeof(uint32_t));
3102 } else if (msg == NS_RESIZE_EVENT) {
3103 mIsHandlingResizeEvent = true;
3104 } else if (msg == NS_MOUSE_BUTTON_DOWN &&
3105 aVisitor.mEvent->mFlags.mIsTrusted) {
3106 gMouseDown = true;
3107 } else if ((msg == NS_MOUSE_BUTTON_UP ||
3108 msg == NS_DRAGDROP_END) &&
3109 aVisitor.mEvent->mFlags.mIsTrusted) {
3110 gMouseDown = false;
3111 if (gDragServiceDisabled) {
3112 nsCOMPtr<nsIDragService> ds =
3113 do_GetService("@mozilla.org/widget/dragservice;1");
3114 if (ds) {
3115 gDragServiceDisabled = false;
3116 ds->Unsuppress();
3121 aVisitor.mParentTarget = GetParentTarget();
3123 // Handle 'active' event.
3124 if (!mIdleObservers.IsEmpty() &&
3125 aVisitor.mEvent->mFlags.mIsTrusted &&
3126 (aVisitor.mEvent->HasMouseEventMessage() ||
3127 aVisitor.mEvent->HasDragEventMessage())) {
3128 mAddActiveEventFuzzTime = false;
3131 return NS_OK;
3134 bool
3135 nsGlobalWindow::ShouldPromptToBlockDialogs()
3137 MOZ_ASSERT(IsOuterWindow());
3139 nsGlobalWindow *topWindow = GetScriptableTop();
3140 if (!topWindow) {
3141 NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?");
3142 return true;
3145 topWindow = topWindow->GetCurrentInnerWindowInternal();
3146 if (!topWindow) {
3147 return true;
3150 return topWindow->DialogsAreBeingAbused();
3153 bool
3154 nsGlobalWindow::AreDialogsEnabled()
3156 MOZ_ASSERT(IsOuterWindow());
3158 nsGlobalWindow *topWindow = GetScriptableTop();
3159 if (!topWindow) {
3160 NS_ERROR("AreDialogsEnabled() called without a top window?");
3161 return false;
3164 // TODO: Warn if no top window?
3165 topWindow = topWindow->GetCurrentInnerWindowInternal();
3166 if (!topWindow) {
3167 return false;
3170 // Dialogs are blocked if the content viewer is hidden
3171 if (mDocShell) {
3172 nsCOMPtr<nsIContentViewer> cv;
3173 mDocShell->GetContentViewer(getter_AddRefs(cv));
3175 bool isHidden;
3176 cv->GetIsHidden(&isHidden);
3177 if (isHidden) {
3178 return false;
3182 return topWindow->mAreDialogsEnabled;
3185 bool
3186 nsGlobalWindow::DialogsAreBeingAbused()
3188 MOZ_ASSERT(IsInnerWindow());
3189 NS_ASSERTION(GetScriptableTop() &&
3190 GetScriptableTop()->GetCurrentInnerWindowInternal() == this,
3191 "DialogsAreBeingAbused called with invalid window");
3193 if (mLastDialogQuitTime.IsNull() ||
3194 nsContentUtils::IsCallerChrome()) {
3195 return false;
3198 TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime);
3199 if (dialogInterval.ToSeconds() <
3200 Preferences::GetInt("dom.successive_dialog_time_limit",
3201 DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) {
3202 mDialogAbuseCount++;
3204 return GetPopupControlState() > openAllowed ||
3205 mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
3208 // Reset the abuse counter
3209 mDialogAbuseCount = 0;
3211 return false;
3214 bool
3215 nsGlobalWindow::ConfirmDialogIfNeeded()
3217 MOZ_ASSERT(IsOuterWindow());
3219 NS_ENSURE_TRUE(mDocShell, false);
3220 nsCOMPtr<nsIPromptService> promptSvc =
3221 do_GetService("@mozilla.org/embedcomp/prompt-service;1");
3223 if (!promptSvc) {
3224 return true;
3227 // Reset popup state while opening a modal dialog, and firing events
3228 // about the dialog, to prevent the current state from being active
3229 // the whole time a modal dialog is open.
3230 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
3232 bool disableDialog = false;
3233 nsXPIDLString label, title;
3234 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
3235 "ScriptDialogLabel", label);
3236 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
3237 "ScriptDialogPreventTitle", title);
3238 promptSvc->Confirm(this, title.get(), label.get(), &disableDialog);
3239 if (disableDialog) {
3240 DisableDialogs();
3241 return false;
3244 return true;
3247 void
3248 nsGlobalWindow::DisableDialogs()
3250 nsGlobalWindow *topWindow = GetScriptableTop();
3251 if (!topWindow) {
3252 NS_ERROR("DisableDialogs() called without a top window?");
3253 return;
3256 topWindow = topWindow->GetCurrentInnerWindowInternal();
3257 // TODO: Warn if no top window?
3258 if (topWindow) {
3259 topWindow->mAreDialogsEnabled = false;
3263 void
3264 nsGlobalWindow::EnableDialogs()
3266 nsGlobalWindow *topWindow = GetScriptableTop();
3267 if (!topWindow) {
3268 NS_ERROR("EnableDialogs() called without a top window?");
3269 return;
3272 // TODO: Warn if no top window?
3273 topWindow = topWindow->GetCurrentInnerWindowInternal();
3274 if (topWindow) {
3275 topWindow->mAreDialogsEnabled = true;
3279 nsresult
3280 nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor)
3282 NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?");
3284 // Return early if there is nothing to do.
3285 switch (aVisitor.mEvent->message) {
3286 case NS_RESIZE_EVENT:
3287 case NS_PAGE_UNLOAD:
3288 case NS_LOAD:
3289 break;
3290 default:
3291 return NS_OK;
3294 /* mChromeEventHandler and mContext go dangling in the middle of this
3295 function under some circumstances (events that destroy the window)
3296 without this addref. */
3297 nsCOMPtr<nsIDOMEventTarget> kungFuDeathGrip1(mChromeEventHandler);
3298 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
3300 if (aVisitor.mEvent->message == NS_RESIZE_EVENT) {
3301 mIsHandlingResizeEvent = false;
3302 } else if (aVisitor.mEvent->message == NS_PAGE_UNLOAD &&
3303 aVisitor.mEvent->mFlags.mIsTrusted) {
3304 // Execute bindingdetached handlers before we tear ourselves
3305 // down.
3306 if (mDoc) {
3307 mDoc->BindingManager()->ExecuteDetachedHandlers();
3309 mIsDocumentLoaded = false;
3310 } else if (aVisitor.mEvent->message == NS_LOAD &&
3311 aVisitor.mEvent->mFlags.mIsTrusted) {
3312 // This is page load event since load events don't propagate to |window|.
3313 // @see nsDocument::PreHandleEvent.
3314 mIsDocumentLoaded = true;
3316 nsCOMPtr<Element> element = GetFrameElementInternal();
3317 nsIDocShell* docShell = GetDocShell();
3318 if (element && GetParentInternal() &&
3319 docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
3320 // If we're not in chrome, or at a chrome boundary, fire the
3321 // onload event for the frame element.
3323 nsEventStatus status = nsEventStatus_eIgnore;
3324 WidgetEvent event(aVisitor.mEvent->mFlags.mIsTrusted, NS_LOAD);
3325 event.mFlags.mBubbles = false;
3327 // Most of the time we could get a pres context to pass in here,
3328 // but not always (i.e. if this window is not shown there won't
3329 // be a pres context available). Since we're not firing a GUI
3330 // event we don't need a pres context anyway so we just pass
3331 // null as the pres context all the time here.
3332 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
3336 return NS_OK;
3339 nsresult
3340 nsGlobalWindow::DispatchDOMEvent(WidgetEvent* aEvent,
3341 nsIDOMEvent* aDOMEvent,
3342 nsPresContext* aPresContext,
3343 nsEventStatus* aEventStatus)
3345 return EventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this),
3346 aEvent, aDOMEvent, aPresContext,
3347 aEventStatus);
3350 void
3351 nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
3353 MOZ_ASSERT(IsOuterWindow());
3354 if (aObject == GetWrapperPreserveColor()) {
3355 PoisonWrapper();
3359 nsresult
3360 nsGlobalWindow::SetArguments(nsIArray *aArguments)
3362 MOZ_ASSERT(IsOuterWindow());
3363 nsresult rv;
3365 // Historically, we've used the same machinery to handle openDialog arguments
3366 // (exposed via window.arguments) and showModalDialog arguments (exposed via
3367 // window.dialogArguments), even though the former is XUL-only and uses an XPCOM
3368 // array while the latter is web-exposed and uses an arbitrary JS value.
3369 // Moreover, per-spec |dialogArguments| is a property of the browsing context
3370 // (outer), whereas |arguments| lives on the inner.
3372 // We've now mostly separated them, but the difference is still opaque to
3373 // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
3374 // embedding waltz we do here).
3376 // So we need to demultiplex the two cases here.
3377 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
3378 if (mIsModalContentWindow) {
3379 // nsWindowWatcher blindly converts the original nsISupports into an array
3380 // of length 1. We need to recover it, and then cast it back to the concrete
3381 // object we know it to be.
3382 nsCOMPtr<nsISupports> supports = do_QueryElementAt(aArguments, 0, &rv);
3383 NS_ENSURE_SUCCESS(rv, rv);
3384 mDialogArguments = static_cast<DialogValueHolder*>(supports.get());
3385 } else {
3386 mArguments = aArguments;
3387 rv = currentInner->DefineArgumentsProperty(aArguments);
3388 NS_ENSURE_SUCCESS(rv, rv);
3391 return NS_OK;
3394 nsresult
3395 nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
3397 MOZ_ASSERT(IsInnerWindow());
3398 MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
3400 nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
3401 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
3402 AutoJSContext cx;
3404 JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
3405 return ctx->SetProperty(obj, "arguments", aArguments);
3408 //*****************************************************************************
3409 // nsGlobalWindow::nsIScriptObjectPrincipal
3410 //*****************************************************************************
3412 nsIPrincipal*
3413 nsGlobalWindow::GetPrincipal()
3415 if (mDoc) {
3416 // If we have a document, get the principal from the document
3417 return mDoc->NodePrincipal();
3420 if (mDocumentPrincipal) {
3421 return mDocumentPrincipal;
3424 // If we don't have a principal and we don't have a document we
3425 // ask the parent window for the principal. This can happen when
3426 // loading a frameset that has a <frame src="javascript:xxx">, in
3427 // that case the global window is used in JS before we've loaded
3428 // a document into the window.
3430 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
3431 do_QueryInterface(GetParentInternal());
3433 if (objPrincipal) {
3434 return objPrincipal->GetPrincipal();
3437 return nullptr;
3440 //*****************************************************************************
3441 // nsGlobalWindow::nsIDOMWindow
3442 //*****************************************************************************
3444 nsIURI*
3445 nsPIDOMWindow::GetDocumentURI() const
3447 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
3450 nsIURI*
3451 nsPIDOMWindow::GetDocBaseURI() const
3453 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
3456 void
3457 nsPIDOMWindow::MaybeCreateDoc()
3459 MOZ_ASSERT(!mDoc);
3460 if (nsIDocShell* docShell = GetDocShell()) {
3461 // Note that |document| here is the same thing as our mDoc, but we
3462 // don't have to explicitly set the member variable because the docshell
3463 // has already called SetNewDocument().
3464 nsCOMPtr<nsIDocument> document = docShell->GetDocument();
3468 Element*
3469 nsPIDOMWindow::GetFrameElementInternal() const
3471 if (mOuterWindow) {
3472 return mOuterWindow->GetFrameElementInternal();
3475 NS_ASSERTION(!IsInnerWindow(),
3476 "GetFrameElementInternal() called on orphan inner window");
3478 return mFrameElement;
3481 void
3482 nsPIDOMWindow::SetFrameElementInternal(Element* aFrameElement)
3484 if (IsOuterWindow()) {
3485 mFrameElement = aFrameElement;
3487 return;
3490 if (!mOuterWindow) {
3491 NS_ERROR("frameElement set on inner window with no outer!");
3493 return;
3496 mOuterWindow->SetFrameElementInternal(aFrameElement);
3499 void
3500 nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
3502 MOZ_ASSERT(IsInnerWindow());
3504 mAudioContexts.AppendElement(aAudioContext);
3506 nsIDocShell* docShell = GetDocShell();
3507 if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) {
3508 aAudioContext->Mute();
3512 void
3513 nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext)
3515 MOZ_ASSERT(IsInnerWindow());
3517 mAudioContexts.RemoveElement(aAudioContext);
3520 void
3521 nsPIDOMWindow::MuteAudioContexts()
3523 MOZ_ASSERT(IsInnerWindow());
3525 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
3526 if (!mAudioContexts[i]->IsOffline()) {
3527 mAudioContexts[i]->Mute();
3532 void
3533 nsPIDOMWindow::UnmuteAudioContexts()
3535 MOZ_ASSERT(IsInnerWindow());
3537 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
3538 if (!mAudioContexts[i]->IsOffline()) {
3539 mAudioContexts[i]->Unmute();
3544 NS_IMETHODIMP
3545 nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument)
3547 nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(GetDocument());
3548 document.forget(aDocument);
3549 return NS_OK;
3552 nsIDOMWindow*
3553 nsGlobalWindow::GetWindow(ErrorResult& aError)
3555 FORWARD_TO_OUTER_OR_THROW(GetWindow, (aError), aError, nullptr);
3557 return this;
3560 NS_IMETHODIMP
3561 nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow)
3563 ErrorResult rv;
3564 nsCOMPtr<nsIDOMWindow> window = GetWindow(rv);
3565 window.forget(aWindow);
3567 return rv.ErrorCode();
3570 nsIDOMWindow*
3571 nsGlobalWindow::GetSelf(ErrorResult& aError)
3573 FORWARD_TO_OUTER_OR_THROW(GetSelf, (aError), aError, nullptr);
3575 return this;
3578 NS_IMETHODIMP
3579 nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow)
3581 ErrorResult rv;
3582 nsCOMPtr<nsIDOMWindow> window = GetSelf(rv);
3583 window.forget(aWindow);
3585 return rv.ErrorCode();
3588 Navigator*
3589 nsGlobalWindow::GetNavigator(ErrorResult& aError)
3591 FORWARD_TO_INNER_OR_THROW(GetNavigator, (aError), aError, nullptr);
3593 if (!mNavigator) {
3594 mNavigator = new Navigator(this);
3597 return mNavigator;
3600 NS_IMETHODIMP
3601 nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator)
3603 ErrorResult rv;
3604 nsCOMPtr<nsIDOMNavigator> navigator = GetNavigator(rv);
3605 navigator.forget(aNavigator);
3607 return rv.ErrorCode();
3610 nsScreen*
3611 nsGlobalWindow::GetScreen(ErrorResult& aError)
3613 FORWARD_TO_INNER_OR_THROW(GetScreen, (aError), aError, nullptr);
3615 if (!mScreen) {
3616 mScreen = nsScreen::Create(this);
3617 if (!mScreen) {
3618 aError.Throw(NS_ERROR_UNEXPECTED);
3619 return nullptr;
3623 return mScreen;
3626 NS_IMETHODIMP
3627 nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen)
3629 ErrorResult rv;
3630 nsRefPtr<nsScreen> screen = GetScreen(rv);
3631 screen.forget(aScreen);
3633 return rv.ErrorCode();
3636 nsHistory*
3637 nsGlobalWindow::GetHistory(ErrorResult& aError)
3639 FORWARD_TO_INNER_OR_THROW(GetHistory, (aError), aError, nullptr);
3641 if (!mHistory) {
3642 mHistory = new nsHistory(this);
3645 return mHistory;
3648 NS_IMETHODIMP
3649 nsGlobalWindow::GetHistory(nsISupports** aHistory)
3651 ErrorResult rv;
3652 nsCOMPtr<nsISupports> history = GetHistory(rv);
3653 history.forget(aHistory);
3655 return rv.ErrorCode();
3658 nsPerformance*
3659 nsGlobalWindow::GetPerformance(ErrorResult& aError)
3661 FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr);
3663 nsPerformance* p = nsPIDOMWindow::GetPerformance();
3664 if (!p) {
3665 aError.Throw(NS_ERROR_FAILURE);
3667 return p;
3670 NS_IMETHODIMP
3671 nsGlobalWindow::GetPerformance(nsISupports** aPerformance)
3673 ErrorResult rv;
3674 nsCOMPtr<nsISupports> performance = GetPerformance(rv);
3675 performance.forget(aPerformance);
3677 return rv.ErrorCode();
3680 nsPerformance*
3681 nsPIDOMWindow::GetPerformance()
3683 MOZ_ASSERT(IsInnerWindow());
3684 CreatePerformanceObjectIfNeeded();
3685 return mPerformance;
3688 void
3689 nsPIDOMWindow::CreatePerformanceObjectIfNeeded()
3691 MOZ_ASSERT(IsInnerWindow());
3693 if (mPerformance || !mDoc) {
3694 return;
3696 nsRefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
3697 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
3698 bool timingEnabled = false;
3699 if (!timedChannel ||
3700 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
3701 !timingEnabled) {
3702 timedChannel = nullptr;
3704 if (timing) {
3705 // If we are dealing with an iframe, we will need the parent's performance
3706 // object (so we can add the iframe as a resource of that page).
3707 nsPerformance* parentPerformance = nullptr;
3708 nsCOMPtr<nsIDOMWindow> parentWindow;
3709 GetScriptableParent(getter_AddRefs(parentWindow));
3710 nsCOMPtr<nsPIDOMWindow> parentPWindow = do_GetInterface(parentWindow);
3711 if (GetOuterWindow() != parentPWindow) {
3712 if (parentPWindow && !parentPWindow->IsInnerWindow()) {
3713 parentPWindow = parentPWindow->GetCurrentInnerWindow();
3715 if (parentPWindow) {
3716 parentPerformance = parentPWindow->GetPerformance();
3719 mPerformance =
3720 new nsPerformance(this, timing, timedChannel, parentPerformance);
3724 bool
3725 nsPIDOMWindow::GetAudioMuted() const
3727 if (!IsInnerWindow()) {
3728 return mInnerWindow->GetAudioMuted();
3731 return mAudioMuted;
3734 void
3735 nsPIDOMWindow::SetAudioMuted(bool aMuted)
3737 if (!IsInnerWindow()) {
3738 mInnerWindow->SetAudioMuted(aMuted);
3739 return;
3742 if (mAudioMuted == aMuted) {
3743 return;
3746 mAudioMuted = aMuted;
3747 RefreshMediaElements();
3750 float
3751 nsPIDOMWindow::GetAudioVolume() const
3753 if (!IsInnerWindow()) {
3754 return mInnerWindow->GetAudioVolume();
3757 return mAudioVolume;
3760 nsresult
3761 nsPIDOMWindow::SetAudioVolume(float aVolume)
3763 if (!IsInnerWindow()) {
3764 return mInnerWindow->SetAudioVolume(aVolume);
3767 if (aVolume < 0.0) {
3768 return NS_ERROR_DOM_INDEX_SIZE_ERR;
3771 if (mAudioVolume == aVolume) {
3772 return NS_OK;
3775 mAudioVolume = aVolume;
3776 RefreshMediaElements();
3777 return NS_OK;
3780 float
3781 nsPIDOMWindow::GetAudioGlobalVolume()
3783 float globalVolume = 1.0;
3784 nsCOMPtr<nsPIDOMWindow> window = this;
3786 do {
3787 if (window->GetAudioMuted()) {
3788 return 0;
3791 globalVolume *= window->GetAudioVolume();
3793 nsCOMPtr<nsIDOMWindow> win;
3794 window->GetParent(getter_AddRefs(win));
3795 if (window == win) {
3796 break;
3799 window = do_QueryInterface(win);
3801 // If there is not parent, or we are the toplevel or the volume is
3802 // already 0.0, we don't continue.
3803 } while (window && window != this && globalVolume);
3805 return globalVolume;
3808 void
3809 nsPIDOMWindow::RefreshMediaElements()
3811 nsRefPtr<AudioChannelService> service =
3812 AudioChannelService::GetOrCreateAudioChannelService();
3813 service->RefreshAgentsVolume(this);
3816 void
3817 nsPIDOMWindow::SendAfterRemotePaintIfRequested()
3819 if (!mSendAfterRemotePaint) {
3820 return;
3823 mSendAfterRemotePaint = false;
3825 nsContentUtils::DispatchChromeEvent(GetExtantDoc(),
3826 GetParentTarget(),
3827 NS_LITERAL_STRING("MozAfterRemotePaint"),
3828 false, false);
3831 // nsISpeechSynthesisGetter
3833 #ifdef MOZ_WEBSPEECH
3834 SpeechSynthesis*
3835 nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
3837 FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr);
3839 if (!mSpeechSynthesis) {
3840 mSpeechSynthesis = new SpeechSynthesis(this);
3843 return mSpeechSynthesis;
3846 NS_IMETHODIMP
3847 nsGlobalWindow::GetSpeechSynthesis(nsISupports** aSpeechSynthesis)
3849 ErrorResult rv;
3850 nsCOMPtr<nsISupports> speechSynthesis;
3851 if (Preferences::GetBool("media.webspeech.synth.enabled")) {
3852 speechSynthesis = GetSpeechSynthesis(rv);
3854 speechSynthesis.forget(aSpeechSynthesis);
3856 return rv.ErrorCode();
3858 #endif
3860 already_AddRefed<nsIDOMWindow>
3861 nsGlobalWindow::GetParent(ErrorResult& aError)
3863 FORWARD_TO_OUTER_OR_THROW(GetParent, (aError), aError, nullptr);
3865 if (!mDocShell) {
3866 return nullptr;
3869 nsCOMPtr<nsIDOMWindow> parent;
3870 if (mDocShell->GetIsBrowserOrApp()) {
3871 parent = this;
3872 } else {
3873 aError = GetRealParent(getter_AddRefs(parent));
3876 return parent.forget();
3880 * GetScriptableParent is called when script reads window.parent.
3882 * In contrast to GetRealParent, GetScriptableParent respects <iframe
3883 * mozbrowser> boundaries, so if |this| is contained by an <iframe
3884 * mozbrowser>, we will return |this| as its own parent.
3886 NS_IMETHODIMP
3887 nsGlobalWindow::GetScriptableParent(nsIDOMWindow** aParent)
3889 ErrorResult rv;
3890 nsCOMPtr<nsIDOMWindow> parent = GetParent(rv);
3891 parent.forget(aParent);
3893 return rv.ErrorCode();
3897 * nsIDOMWindow::GetParent (when called from C++) is just a wrapper around
3898 * GetRealParent.
3900 NS_IMETHODIMP
3901 nsGlobalWindow::GetRealParent(nsIDOMWindow** aParent)
3903 FORWARD_TO_OUTER(GetRealParent, (aParent), NS_ERROR_NOT_INITIALIZED);
3905 *aParent = nullptr;
3906 if (!mDocShell) {
3907 return NS_OK;
3910 nsCOMPtr<nsIDocShell> parent;
3911 mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
3913 if (parent) {
3914 nsCOMPtr<nsPIDOMWindow> win = parent->GetWindow();
3915 win.forget(aParent);
3917 else {
3918 *aParent = static_cast<nsIDOMWindow*>(this);
3919 NS_ADDREF(*aParent);
3921 return NS_OK;
3924 static nsresult
3925 GetTopImpl(nsGlobalWindow* aWin, nsIDOMWindow** aTop, bool aScriptable)
3927 *aTop = nullptr;
3929 // Walk up the parent chain.
3931 nsCOMPtr<nsIDOMWindow> prevParent = aWin;
3932 nsCOMPtr<nsIDOMWindow> parent = aWin;
3933 do {
3934 if (!parent) {
3935 break;
3938 prevParent = parent;
3940 nsCOMPtr<nsIDOMWindow> newParent;
3941 nsresult rv;
3942 if (aScriptable) {
3943 rv = parent->GetScriptableParent(getter_AddRefs(newParent));
3945 else {
3946 rv = parent->GetParent(getter_AddRefs(newParent));
3948 NS_ENSURE_SUCCESS(rv, rv);
3950 parent = newParent;
3952 } while (parent != prevParent);
3954 if (parent) {
3955 parent.swap(*aTop);
3958 return NS_OK;
3962 * GetScriptableTop is called when script reads window.top.
3964 * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser>
3965 * boundaries. If we encounter a window owned by an <iframe mozbrowser> while
3966 * walking up the window hierarchy, we'll stop and return that window.
3968 NS_IMETHODIMP
3969 nsGlobalWindow::GetScriptableTop(nsIDOMWindow **aTop)
3971 FORWARD_TO_OUTER(GetScriptableTop, (aTop), NS_ERROR_NOT_INITIALIZED);
3972 return GetTopImpl(this, aTop, /* aScriptable = */ true);
3976 * nsIDOMWindow::GetTop (when called from C++) is just a wrapper around
3977 * GetRealTop.
3979 NS_IMETHODIMP
3980 nsGlobalWindow::GetRealTop(nsIDOMWindow** aTop)
3982 nsGlobalWindow* outer;
3983 if (IsInnerWindow()) {
3984 outer = GetOuterWindowInternal();
3985 if (!outer) {
3986 NS_WARNING("No outer window available!");
3987 return NS_ERROR_NOT_INITIALIZED;
3989 } else {
3990 outer = this;
3992 return GetTopImpl(outer, aTop, /* aScriptable = */ false);
3995 void
3996 nsGlobalWindow::GetContent(JSContext* aCx,
3997 JS::MutableHandle<JSObject*> aRetval,
3998 ErrorResult& aError)
4000 FORWARD_TO_OUTER_OR_THROW(GetContent, (aCx, aRetval, aError), aError, );
4002 nsCOMPtr<nsIDOMWindow> content = GetContentInternal(aError);
4003 if (aError.Failed()) {
4004 return;
4007 if (content) {
4008 JS::Rooted<JS::Value> val(aCx);
4009 aError = nsContentUtils::WrapNative(aCx, content, &val);
4010 if (aError.Failed()) {
4011 return;
4014 aRetval.set(&val.toObject());
4015 return;
4018 aRetval.set(nullptr);
4019 return;
4022 already_AddRefed<nsIDOMWindow>
4023 nsGlobalWindow::GetContentInternal(ErrorResult& aError)
4025 // First check for a named frame named "content"
4026 nsCOMPtr<nsIDOMWindow> domWindow =
4027 GetChildWindow(NS_LITERAL_STRING("content"));
4028 if (domWindow) {
4029 return domWindow.forget();
4032 // If we're contained in <iframe mozbrowser> or <iframe mozapp>, then
4033 // GetContent is the same as window.top.
4034 if (mDocShell && mDocShell->GetIsInBrowserOrApp()) {
4035 return GetTop(aError);
4038 nsCOMPtr<nsIDocShellTreeItem> primaryContent;
4039 if (!nsContentUtils::IsCallerChrome()) {
4040 // If we're called by non-chrome code, make sure we don't return
4041 // the primary content window if the calling tab is hidden. In
4042 // such a case we return the same-type root in the hidden tab,
4043 // which is "good enough", for now.
4044 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell));
4046 if (baseWin) {
4047 bool visible = false;
4048 baseWin->GetVisibility(&visible);
4050 if (!visible) {
4051 mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(primaryContent));
4056 if (!primaryContent) {
4057 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
4058 if (!treeOwner) {
4059 aError.Throw(NS_ERROR_FAILURE);
4060 return nullptr;
4063 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
4066 if (!primaryContent) {
4067 return nullptr;
4070 domWindow = primaryContent->GetWindow();
4071 return domWindow.forget();
4074 NS_IMETHODIMP
4075 nsGlobalWindow::GetContent(nsIDOMWindow** aContent)
4077 ErrorResult rv;
4078 *aContent = GetContentInternal(rv).take();
4080 return rv.ErrorCode();
4083 MozSelfSupport*
4084 nsGlobalWindow::GetMozSelfSupport(ErrorResult& aError)
4086 if (mMozSelfSupport) {
4087 return mMozSelfSupport;
4090 AutoSafeJSContext cx;
4091 GlobalObject global(cx, FastGetGlobalJSObject());
4092 mMozSelfSupport = MozSelfSupport::Constructor(global, cx, aError);
4093 return mMozSelfSupport;
4096 NS_IMETHODIMP
4097 nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
4099 ErrorResult rv;
4100 JS::Rooted<JSObject*> content(aCx);
4101 GetContent(aCx, &content, rv);
4102 if (!rv.Failed()) {
4103 aVal.setObjectOrNull(content);
4106 return rv.ErrorCode();
4109 NS_IMETHODIMP
4110 nsGlobalWindow::GetPrompter(nsIPrompt** aPrompt)
4112 if (IsInnerWindow()) {
4113 nsGlobalWindow* outer = GetOuterWindowInternal();
4114 if (!outer) {
4115 NS_WARNING("No outer window available!");
4116 return NS_ERROR_NOT_INITIALIZED;
4118 return outer->GetPrompter(aPrompt);
4121 if (!mDocShell)
4122 return NS_ERROR_FAILURE;
4124 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
4125 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
4127 NS_ADDREF(*aPrompt = prompter);
4128 return NS_OK;
4131 BarProp*
4132 nsGlobalWindow::GetMenubar(ErrorResult& aError)
4134 FORWARD_TO_INNER_OR_THROW(GetMenubar, (aError), aError, nullptr);
4136 if (!mMenubar) {
4137 mMenubar = new MenubarProp(this);
4140 return mMenubar;
4143 NS_IMETHODIMP
4144 nsGlobalWindow::GetMenubar(nsISupports** aMenubar)
4146 ErrorResult rv;
4147 nsCOMPtr<nsISupports> menubar = GetMenubar(rv);
4148 menubar.forget(aMenubar);
4150 return rv.ErrorCode();
4153 BarProp*
4154 nsGlobalWindow::GetToolbar(ErrorResult& aError)
4156 FORWARD_TO_INNER_OR_THROW(GetToolbar, (aError), aError, nullptr);
4158 if (!mToolbar) {
4159 mToolbar = new ToolbarProp(this);
4162 return mToolbar;
4165 NS_IMETHODIMP
4166 nsGlobalWindow::GetToolbar(nsISupports** aToolbar)
4168 ErrorResult rv;
4169 nsCOMPtr<nsISupports> toolbar = GetToolbar(rv);
4170 toolbar.forget(aToolbar);
4172 return rv.ErrorCode();
4175 BarProp*
4176 nsGlobalWindow::GetLocationbar(ErrorResult& aError)
4178 FORWARD_TO_INNER_OR_THROW(GetLocationbar, (aError), aError, nullptr);
4180 if (!mLocationbar) {
4181 mLocationbar = new LocationbarProp(this);
4183 return mLocationbar;
4186 NS_IMETHODIMP
4187 nsGlobalWindow::GetLocationbar(nsISupports** aLocationbar)
4189 ErrorResult rv;
4190 nsCOMPtr<nsISupports> locationbar = GetLocationbar(rv);
4191 locationbar.forget(aLocationbar);
4193 return rv.ErrorCode();
4196 BarProp*
4197 nsGlobalWindow::GetPersonalbar(ErrorResult& aError)
4199 FORWARD_TO_INNER_OR_THROW(GetPersonalbar, (aError), aError, nullptr);
4201 if (!mPersonalbar) {
4202 mPersonalbar = new PersonalbarProp(this);
4204 return mPersonalbar;
4207 NS_IMETHODIMP
4208 nsGlobalWindow::GetPersonalbar(nsISupports** aPersonalbar)
4210 ErrorResult rv;
4211 nsCOMPtr<nsISupports> personalbar = GetPersonalbar(rv);
4212 personalbar.forget(aPersonalbar);
4214 return rv.ErrorCode();
4217 BarProp*
4218 nsGlobalWindow::GetStatusbar(ErrorResult& aError)
4220 FORWARD_TO_INNER_OR_THROW(GetStatusbar, (aError), aError, nullptr);
4222 if (!mStatusbar) {
4223 mStatusbar = new StatusbarProp(this);
4225 return mStatusbar;
4228 NS_IMETHODIMP
4229 nsGlobalWindow::GetStatusbar(nsISupports** aStatusbar)
4231 ErrorResult rv;
4232 nsCOMPtr<nsISupports> statusbar = GetStatusbar(rv);
4233 statusbar.forget(aStatusbar);
4235 return rv.ErrorCode();
4238 BarProp*
4239 nsGlobalWindow::GetScrollbars(ErrorResult& aError)
4241 FORWARD_TO_INNER_OR_THROW(GetScrollbars, (aError), aError, nullptr);
4243 if (!mScrollbars) {
4244 mScrollbars = new ScrollbarsProp(this);
4247 return mScrollbars;
4250 NS_IMETHODIMP
4251 nsGlobalWindow::GetScrollbars(nsISupports** aScrollbars)
4253 ErrorResult rv;
4254 nsCOMPtr<nsISupports> scrollbars = GetScrollbars(rv);
4255 scrollbars.forget(aScrollbars);
4257 return rv.ErrorCode();
4260 bool
4261 nsGlobalWindow::GetClosed(ErrorResult& aError)
4263 FORWARD_TO_OUTER_OR_THROW(GetClosed, (aError), aError, false);
4265 // If someone called close(), or if we don't have a docshell, we're closed.
4266 return mIsClosed || !mDocShell;
4269 NS_IMETHODIMP
4270 nsGlobalWindow::GetClosed(bool* aClosed)
4272 ErrorResult rv;
4273 *aClosed = GetClosed(rv);
4275 return rv.ErrorCode();
4278 nsDOMWindowList*
4279 nsGlobalWindow::GetWindowList()
4281 MOZ_ASSERT(IsOuterWindow());
4283 if (!mFrames && mDocShell) {
4284 mFrames = new nsDOMWindowList(mDocShell);
4287 return mFrames;
4290 NS_IMETHODIMP
4291 nsGlobalWindow::GetFrames(nsIDOMWindowCollection** aFrames)
4293 FORWARD_TO_OUTER(GetFrames, (aFrames), NS_ERROR_NOT_INITIALIZED);
4295 *aFrames = GetWindowList();
4296 NS_IF_ADDREF(*aFrames);
4297 return NS_OK;
4300 already_AddRefed<nsIDOMWindow>
4301 nsGlobalWindow::IndexedGetter(uint32_t aIndex, bool& aFound)
4303 aFound = false;
4305 FORWARD_TO_OUTER(IndexedGetter, (aIndex, aFound), nullptr);
4307 nsDOMWindowList* windows = GetWindowList();
4308 NS_ENSURE_TRUE(windows, nullptr);
4310 return windows->IndexedGetter(aIndex, aFound);
4313 void
4314 nsGlobalWindow::GetSupportedNames(nsTArray<nsString>& aNames)
4316 FORWARD_TO_OUTER_VOID(GetSupportedNames, (aNames));
4318 nsDOMWindowList* windows = GetWindowList();
4319 if (windows) {
4320 uint32_t length = windows->GetLength();
4321 nsString* name = aNames.AppendElements(length);
4322 for (uint32_t i = 0; i < length; ++i, ++name) {
4323 nsCOMPtr<nsIDocShellTreeItem> item =
4324 windows->GetDocShellTreeItemAt(i);
4325 item->GetName(*name);
4330 bool
4331 nsGlobalWindow::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
4332 JS::Handle<jsid> aId,
4333 JS::MutableHandle<JSPropertyDescriptor> aDesc)
4335 MOZ_ASSERT(IsInnerWindow());
4337 // Note: The infallibleInit call in GlobalResolve depends on this check.
4338 if (!JSID_IS_STRING(aId)) {
4339 return true;
4342 nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc);
4343 if (NS_FAILED(rv)) {
4344 return Throw(aCx, rv);
4347 return true;
4350 struct GlobalNameEnumeratorClosure
4352 GlobalNameEnumeratorClosure(JSContext* aCx, nsGlobalWindow* aWindow,
4353 nsTArray<nsString>& aNames)
4354 : mCx(aCx),
4355 mWindow(aWindow),
4356 mWrapper(aCx, aWindow->GetWrapper()),
4357 mNames(aNames)
4361 JSContext* mCx;
4362 nsGlobalWindow* mWindow;
4363 JS::Rooted<JSObject*> mWrapper;
4364 nsTArray<nsString>& mNames;
4367 static PLDHashOperator
4368 EnumerateGlobalName(const nsAString& aName,
4369 const nsGlobalNameStruct& aNameStruct,
4370 void* aClosure)
4372 GlobalNameEnumeratorClosure* closure =
4373 static_cast<GlobalNameEnumeratorClosure*>(aClosure);
4375 if (nsWindowSH::NameStructEnabled(closure->mCx, closure->mWindow, aName,
4376 aNameStruct) &&
4377 (!aNameStruct.mConstructorEnabled ||
4378 aNameStruct.mConstructorEnabled(closure->mCx, closure->mWrapper))) {
4379 closure->mNames.AppendElement(aName);
4381 return PL_DHASH_NEXT;
4384 void
4385 nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
4386 ErrorResult& aRv)
4388 // "Components" is marked as enumerable but only resolved on demand :-/.
4389 //aNames.AppendElement(NS_LITERAL_STRING("Components"));
4391 nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
4392 if (nameSpaceManager) {
4393 GlobalNameEnumeratorClosure closure(aCx, this, aNames);
4394 nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &closure);
4398 /* static */ bool
4399 nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
4401 // For now, have to deal with XPConnect objects here.
4402 return xpc::WindowOrNull(aObj)->IsChromeWindow();
4405 /* static */ bool
4406 nsGlobalWindow::IsShowModalDialogEnabled(JSContext*, JSObject*)
4408 static bool sAddedPrefCache = false;
4409 static bool sIsDisabled;
4410 static const char sShowModalDialogPref[] = "dom.disable_window_showModalDialog";
4412 if (!sAddedPrefCache) {
4413 Preferences::AddBoolVarCache(&sIsDisabled, sShowModalDialogPref, false);
4414 sAddedPrefCache = true;
4417 return !sIsDisabled;
4420 nsIDOMOfflineResourceList*
4421 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
4423 FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr);
4425 if (!mApplicationCache) {
4426 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
4427 if (!webNav) {
4428 aError.Throw(NS_ERROR_FAILURE);
4429 return nullptr;
4432 nsCOMPtr<nsIURI> uri;
4433 aError = webNav->GetCurrentURI(getter_AddRefs(uri));
4434 if (aError.Failed()) {
4435 return nullptr;
4438 nsCOMPtr<nsIURI> manifestURI;
4439 nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));
4441 nsRefPtr<nsDOMOfflineResourceList> applicationCache =
4442 new nsDOMOfflineResourceList(manifestURI, uri, this);
4444 applicationCache->Init();
4446 mApplicationCache = applicationCache;
4449 return mApplicationCache;
4452 NS_IMETHODIMP
4453 nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCache)
4455 ErrorResult rv;
4456 nsCOMPtr<nsIDOMOfflineResourceList> applicationCache =
4457 GetApplicationCache(rv);
4458 applicationCache.forget(aApplicationCache);
4460 return rv.ErrorCode();
4463 nsIDOMCrypto*
4464 nsGlobalWindow::GetCrypto(ErrorResult& aError)
4466 FORWARD_TO_INNER_OR_THROW(GetCrypto, (aError), aError, nullptr);
4468 if (!mCrypto) {
4469 #ifndef MOZ_DISABLE_CRYPTOLEGACY
4470 if (XRE_GetProcessType() != GeckoProcessType_Content) {
4471 nsresult rv;
4472 mCrypto = do_CreateInstance(NS_CRYPTO_CONTRACTID, &rv);
4473 if (NS_FAILED(rv)) {
4474 aError.Throw(rv);
4475 return nullptr;
4477 } else
4478 #endif
4480 mCrypto = new Crypto();
4483 mCrypto->Init(this);
4485 return mCrypto;
4488 NS_IMETHODIMP
4489 nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto)
4491 ErrorResult rv;
4492 nsCOMPtr<nsIDOMCrypto> crypto = GetCrypto(rv);
4493 crypto.forget(aCrypto);
4495 return rv.ErrorCode();
4498 nsIControllers*
4499 nsGlobalWindow::GetControllers(ErrorResult& aError)
4501 FORWARD_TO_OUTER_OR_THROW(GetControllers, (aError), aError, nullptr);
4503 if (!mControllers) {
4504 nsresult rv;
4505 mControllers = do_CreateInstance(kXULControllersCID, &rv);
4506 if (NS_FAILED(rv)) {
4507 aError.Throw(rv);
4508 return nullptr;
4511 // Add in the default controller
4512 nsCOMPtr<nsIController> controller = do_CreateInstance(
4513 NS_WINDOWCONTROLLER_CONTRACTID, &rv);
4514 if (NS_FAILED(rv)) {
4515 aError.Throw(rv);
4516 return nullptr;
4519 mControllers->InsertControllerAt(0, controller);
4520 nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller);
4521 if (!controllerContext) {
4522 aError.Throw(NS_ERROR_FAILURE);
4523 return nullptr;
4526 controllerContext->SetCommandContext(static_cast<nsIDOMWindow*>(this));
4529 return mControllers;
4532 NS_IMETHODIMP
4533 nsGlobalWindow::GetControllers(nsIControllers** aResult)
4535 ErrorResult rv;
4536 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
4537 controllers.forget(aResult);
4539 return rv.ErrorCode();
4542 nsIDOMWindow*
4543 nsGlobalWindow::GetOpenerWindow(ErrorResult& aError)
4545 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindow, (aError), aError, nullptr);
4547 nsCOMPtr<nsPIDOMWindow> opener = do_QueryReferent(mOpener);
4548 if (!opener) {
4549 return nullptr;
4552 // First, check if we were called from a privileged chrome script
4553 if (nsContentUtils::IsCallerChrome()) {
4554 return opener;
4557 // First, ensure that we're not handing back a chrome window.
4558 nsGlobalWindow *win = static_cast<nsGlobalWindow *>(opener.get());
4559 if (win->IsChromeWindow()) {
4560 return nullptr;
4563 // We don't want to reveal the opener if the opener is a mail window,
4564 // because opener can be used to spoof the contents of a message (bug 105050).
4565 // So, we look in the opener's root docshell to see if it's a mail window.
4566 nsCOMPtr<nsIDocShell> openerDocShell = opener->GetDocShell();
4568 if (openerDocShell) {
4569 nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
4570 openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem));
4571 nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
4572 if (openerRootDocShell) {
4573 uint32_t appType;
4574 nsresult rv = openerRootDocShell->GetAppType(&appType);
4575 if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) {
4576 return opener;
4581 return nullptr;
4584 void
4585 nsGlobalWindow::GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
4586 ErrorResult& aError)
4588 nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(aError);
4589 if (aError.Failed() || !opener) {
4590 aRetval.setNull();
4591 return;
4594 aError = nsContentUtils::WrapNative(aCx, opener, aRetval);
4597 NS_IMETHODIMP
4598 nsGlobalWindow::GetScriptableOpener(JSContext* aCx,
4599 JS::MutableHandle<JS::Value> aOpener)
4601 ErrorResult rv;
4602 GetOpener(aCx, aOpener, rv);
4604 return rv.ErrorCode();
4607 NS_IMETHODIMP
4608 nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener)
4610 ErrorResult rv;
4611 nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(rv);
4612 opener.forget(aOpener);
4613 return rv.ErrorCode();
4616 void
4617 nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
4618 ErrorResult& aError)
4620 // Check if we were called from a privileged chrome script. If not, and if
4621 // aOpener is not null, just define aOpener on our inner window's JS object,
4622 // wrapped into the current compartment so that for Xrays we define on the
4623 // Xray expando object, but don't set it on the outer window, so that it'll
4624 // get reset on navigation. This is just like replaceable properties, but
4625 // we're not quite readonly.
4626 if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
4627 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
4628 if (!thisObj) {
4629 aError.Throw(NS_ERROR_UNEXPECTED);
4630 return;
4633 if (!JS_WrapObject(aCx, &thisObj) ||
4634 !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
4635 JS_PropertyStub, JS_StrictPropertyStub)) {
4636 aError.Throw(NS_ERROR_FAILURE);
4639 return;
4642 if (!aOpener.isObjectOrNull()) {
4643 // Chrome code trying to set some random value as opener
4644 aError.Throw(NS_ERROR_INVALID_ARG);
4645 return;
4648 nsPIDOMWindow* win = nullptr;
4649 if (aOpener.isObject()) {
4650 JSObject* unwrapped = js::CheckedUnwrap(&aOpener.toObject(),
4651 /* stopAtOuter = */ false);
4652 if (!unwrapped) {
4653 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4654 return;
4657 win = xpc::WindowOrNull(unwrapped);
4658 if (!win) {
4659 // Wasn't a window
4660 aError.Throw(NS_ERROR_INVALID_ARG);
4661 return;
4665 if (win && win->IsInnerWindow()) {
4666 if (!win->IsCurrentInnerWindow()) {
4667 aError.Throw(NS_ERROR_FAILURE);
4668 return;
4670 win = win->GetOuterWindow();
4673 SetOpenerWindow(win, false);
4676 NS_IMETHODIMP
4677 nsGlobalWindow::SetScriptableOpener(JSContext* aCx,
4678 JS::Handle<JS::Value> aOpener)
4680 ErrorResult rv;
4681 SetOpener(aCx, aOpener, rv);
4683 return rv.ErrorCode();
4686 NS_IMETHODIMP
4687 nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener)
4689 SetOpenerWindow(aOpener, false);
4690 return NS_OK;
4693 void
4694 nsGlobalWindow::GetStatus(nsAString& aStatus, ErrorResult& aError)
4696 FORWARD_TO_OUTER_OR_THROW(GetStatus, (aStatus, aError), aError, );
4698 aStatus = mStatus;
4701 NS_IMETHODIMP
4702 nsGlobalWindow::GetStatus(nsAString& aStatus)
4704 ErrorResult rv;
4705 GetStatus(aStatus, rv);
4707 return rv.ErrorCode();
4710 void
4711 nsGlobalWindow::SetStatus(const nsAString& aStatus, ErrorResult& aError)
4713 FORWARD_TO_OUTER_OR_THROW(SetStatus, (aStatus, aError), aError, );
4715 mStatus = aStatus;
4718 * If caller is not chrome and dom.disable_window_status_change is true,
4719 * prevent propagating window.status to the UI by exiting early
4722 if (!CanSetProperty("dom.disable_window_status_change")) {
4723 return;
4726 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
4727 if (browserChrome) {
4728 browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
4729 PromiseFlatString(aStatus).get());
4733 NS_IMETHODIMP
4734 nsGlobalWindow::SetStatus(const nsAString& aStatus)
4736 ErrorResult rv;
4737 SetStatus(aStatus, rv);
4739 return rv.ErrorCode();
4742 void
4743 nsGlobalWindow::GetName(nsAString& aName, ErrorResult& aError)
4745 FORWARD_TO_OUTER_OR_THROW(GetName, (aName, aError), aError, );
4747 if (mDocShell) {
4748 mDocShell->GetName(aName);
4752 NS_IMETHODIMP
4753 nsGlobalWindow::GetName(nsAString& aName)
4755 ErrorResult rv;
4756 GetName(aName, rv);
4758 return rv.ErrorCode();
4761 void
4762 nsGlobalWindow::SetName(const nsAString& aName, mozilla::ErrorResult& aError)
4764 FORWARD_TO_OUTER_OR_THROW(SetName, (aName, aError), aError, );
4766 if (mDocShell) {
4767 aError = mDocShell->SetName(aName);
4771 NS_IMETHODIMP
4772 nsGlobalWindow::SetName(const nsAString& aName)
4774 ErrorResult rv;
4775 SetName(aName, rv);
4777 return rv.ErrorCode();
4780 // Helper functions used by many methods below.
4781 int32_t
4782 nsGlobalWindow::DevToCSSIntPixels(int32_t px)
4784 if (!mDocShell)
4785 return px; // assume 1:1
4787 nsRefPtr<nsPresContext> presContext;
4788 mDocShell->GetPresContext(getter_AddRefs(presContext));
4789 if (!presContext)
4790 return px;
4792 return presContext->DevPixelsToIntCSSPixels(px);
4795 int32_t
4796 nsGlobalWindow::CSSToDevIntPixels(int32_t px)
4798 if (!mDocShell)
4799 return px; // assume 1:1
4801 nsRefPtr<nsPresContext> presContext;
4802 mDocShell->GetPresContext(getter_AddRefs(presContext));
4803 if (!presContext)
4804 return px;
4806 return presContext->CSSPixelsToDevPixels(px);
4809 nsIntSize
4810 nsGlobalWindow::DevToCSSIntPixels(nsIntSize px)
4812 if (!mDocShell)
4813 return px; // assume 1:1
4815 nsRefPtr<nsPresContext> presContext;
4816 mDocShell->GetPresContext(getter_AddRefs(presContext));
4817 if (!presContext)
4818 return px;
4820 return nsIntSize(
4821 presContext->DevPixelsToIntCSSPixels(px.width),
4822 presContext->DevPixelsToIntCSSPixels(px.height));
4825 nsIntSize
4826 nsGlobalWindow::CSSToDevIntPixels(nsIntSize px)
4828 if (!mDocShell)
4829 return px; // assume 1:1
4831 nsRefPtr<nsPresContext> presContext;
4832 mDocShell->GetPresContext(getter_AddRefs(presContext));
4833 if (!presContext)
4834 return px;
4836 return nsIntSize(
4837 presContext->CSSPixelsToDevPixels(px.width),
4838 presContext->CSSPixelsToDevPixels(px.height));
4841 nsresult
4842 nsGlobalWindow::GetInnerSize(CSSIntSize& aSize)
4844 MOZ_ASSERT(IsOuterWindow());
4846 EnsureSizeUpToDate();
4848 NS_ENSURE_STATE(mDocShell);
4850 nsRefPtr<nsPresContext> presContext;
4851 mDocShell->GetPresContext(getter_AddRefs(presContext));
4852 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4854 if (!presContext || !presShell) {
4855 aSize = CSSIntSize(0, 0);
4856 return NS_OK;
4860 * On platforms with resolution-based zooming, the CSS viewport
4861 * and visual viewport may not be the same. The inner size should
4862 * be the visual viewport, but we fall back to the CSS viewport
4863 * if it is not set.
4865 if (presShell->IsScrollPositionClampingScrollPortSizeSet()) {
4866 aSize = CSSIntRect::FromAppUnitsRounded(
4867 presShell->GetScrollPositionClampingScrollPortSize());
4868 } else {
4869 nsRefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4870 if (viewManager) {
4871 viewManager->FlushDelayedResize(false);
4874 aSize = CSSIntRect::FromAppUnitsRounded(
4875 presContext->GetVisibleArea().Size());
4877 return NS_OK;
4880 int32_t
4881 nsGlobalWindow::GetInnerWidth(ErrorResult& aError)
4883 FORWARD_TO_OUTER_OR_THROW(GetInnerWidth, (aError), aError, 0);
4885 CSSIntSize size;
4886 aError = GetInnerSize(size);
4887 return size.width;
4890 NS_IMETHODIMP
4891 nsGlobalWindow::GetInnerWidth(int32_t* aInnerWidth)
4893 ErrorResult rv;
4894 *aInnerWidth = GetInnerWidth(rv);
4896 return rv.ErrorCode();
4899 void
4900 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth, ErrorResult& aError)
4902 FORWARD_TO_OUTER_OR_THROW(SetInnerWidth, (aInnerWidth, aError), aError, );
4904 if (!mDocShell) {
4905 aError.Throw(NS_ERROR_UNEXPECTED);
4906 return;
4910 * If caller is not chrome and the user has not explicitly exempted the site,
4911 * prevent setting window.innerWidth by exiting early
4913 if (!CanMoveResizeWindows() || IsFrame()) {
4914 return;
4917 CheckSecurityWidthAndHeight(&aInnerWidth, nullptr);
4919 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4921 if (presShell && presShell->GetIsViewportOverridden())
4923 nscoord height = 0;
4925 nsRefPtr<nsPresContext> presContext;
4926 presContext = presShell->GetPresContext();
4928 nsRect shellArea = presContext->GetVisibleArea();
4929 height = shellArea.height;
4930 SetCSSViewportWidthAndHeight(nsPresContext::CSSPixelsToAppUnits(aInnerWidth),
4931 height);
4932 return;
4935 int32_t height = 0;
4936 int32_t unused = 0;
4938 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
4939 docShellAsWin->GetSize(&unused, &height);
4940 aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height);
4943 NS_IMETHODIMP
4944 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth)
4946 ErrorResult rv;
4947 SetInnerWidth(aInnerWidth, rv);
4949 return rv.ErrorCode();
4952 int32_t
4953 nsGlobalWindow::GetInnerHeight(ErrorResult& aError)
4955 FORWARD_TO_OUTER_OR_THROW(GetInnerHeight, (aError), aError, 0);
4957 CSSIntSize size;
4958 aError = GetInnerSize(size);
4959 return size.height;
4962 NS_IMETHODIMP
4963 nsGlobalWindow::GetInnerHeight(int32_t* aInnerHeight)
4965 ErrorResult rv;
4966 *aInnerHeight = GetInnerHeight(rv);
4968 return rv.ErrorCode();
4971 void
4972 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight, ErrorResult& aError)
4974 FORWARD_TO_OUTER_OR_THROW(SetInnerHeight, (aInnerHeight, aError), aError, );
4976 if (!mDocShell) {
4977 aError.Throw(NS_ERROR_UNEXPECTED);
4978 return;
4982 * If caller is not chrome and the user has not explicitly exempted the site,
4983 * prevent setting window.innerHeight by exiting early
4985 if (!CanMoveResizeWindows() || IsFrame()) {
4986 return;
4989 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4991 if (presShell && presShell->GetIsViewportOverridden())
4993 nsRefPtr<nsPresContext> presContext;
4994 presContext = presShell->GetPresContext();
4996 nsRect shellArea = presContext->GetVisibleArea();
4997 nscoord height = aInnerHeight;
4998 nscoord width = shellArea.width;
4999 CheckSecurityWidthAndHeight(nullptr, &height);
5000 SetCSSViewportWidthAndHeight(width,
5001 nsPresContext::CSSPixelsToAppUnits(height));
5002 return;
5005 int32_t height = 0;
5006 int32_t width = 0;
5008 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
5009 docShellAsWin->GetSize(&width, &height);
5010 CheckSecurityWidthAndHeight(nullptr, &aInnerHeight);
5011 aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
5014 NS_IMETHODIMP
5015 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight)
5017 ErrorResult rv;
5018 SetInnerHeight(aInnerHeight, rv);
5020 return rv.ErrorCode();
5023 nsIntSize
5024 nsGlobalWindow::GetOuterSize(ErrorResult& aError)
5026 MOZ_ASSERT(IsOuterWindow());
5028 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5029 if (!treeOwnerAsWin) {
5030 aError.Throw(NS_ERROR_FAILURE);
5031 return nsIntSize(0, 0);
5034 nsGlobalWindow* rootWindow =
5035 static_cast<nsGlobalWindow *>(GetPrivateRoot());
5036 if (rootWindow) {
5037 rootWindow->FlushPendingNotifications(Flush_Layout);
5040 nsIntSize sizeDevPixels;
5041 aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
5042 if (aError.Failed()) {
5043 return nsIntSize();
5046 return DevToCSSIntPixels(sizeDevPixels);
5049 int32_t
5050 nsGlobalWindow::GetOuterWidth(ErrorResult& aError)
5052 FORWARD_TO_OUTER_OR_THROW(GetOuterWidth, (aError), aError, 0);
5053 return GetOuterSize(aError).width;
5056 NS_IMETHODIMP
5057 nsGlobalWindow::GetOuterWidth(int32_t* aOuterWidth)
5059 ErrorResult rv;
5060 *aOuterWidth = GetOuterWidth(rv);
5062 return rv.ErrorCode();
5065 int32_t
5066 nsGlobalWindow::GetOuterHeight(ErrorResult& aError)
5068 FORWARD_TO_OUTER_OR_THROW(GetOuterHeight, (aError), aError, 0);
5069 return GetOuterSize(aError).height;
5072 NS_IMETHODIMP
5073 nsGlobalWindow::GetOuterHeight(int32_t* aOuterHeight)
5075 ErrorResult rv;
5076 *aOuterHeight = GetOuterHeight(rv);
5078 return rv.ErrorCode();
5081 void
5082 nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
5083 ErrorResult& aError)
5085 MOZ_ASSERT(IsOuterWindow());
5088 * If caller is not chrome and the user has not explicitly exempted the site,
5089 * prevent setting window.outerWidth by exiting early
5092 if (!CanMoveResizeWindows() || IsFrame()) {
5093 return;
5096 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5097 if (!treeOwnerAsWin) {
5098 aError.Throw(NS_ERROR_FAILURE);
5099 return;
5102 CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
5103 aIsWidth ? nullptr : &aLengthCSSPixels);
5105 int32_t width, height;
5106 aError = treeOwnerAsWin->GetSize(&width, &height);
5107 if (aError.Failed()) {
5108 return;
5111 int32_t lengthDevPixels = CSSToDevIntPixels(aLengthCSSPixels);
5112 if (aIsWidth) {
5113 width = lengthDevPixels;
5114 } else {
5115 height = lengthDevPixels;
5117 aError = treeOwnerAsWin->SetSize(width, height, true);
5120 void
5121 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth, ErrorResult& aError)
5123 FORWARD_TO_OUTER_OR_THROW(SetOuterWidth, (aOuterWidth, aError), aError, );
5125 SetOuterSize(aOuterWidth, true, aError);
5128 NS_IMETHODIMP
5129 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth)
5131 ErrorResult rv;
5132 SetOuterWidth(aOuterWidth, rv);
5134 return rv.ErrorCode();
5137 void
5138 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight, ErrorResult& aError)
5140 FORWARD_TO_OUTER_OR_THROW(SetOuterHeight, (aOuterHeight, aError), aError, );
5142 SetOuterSize(aOuterHeight, false, aError);
5145 NS_IMETHODIMP
5146 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight)
5148 ErrorResult rv;
5149 SetOuterHeight(aOuterHeight, rv);
5151 return rv.ErrorCode();
5154 nsIntPoint
5155 nsGlobalWindow::GetScreenXY(ErrorResult& aError)
5157 MOZ_ASSERT(IsOuterWindow());
5159 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5160 if (!treeOwnerAsWin) {
5161 aError.Throw(NS_ERROR_FAILURE);
5162 return nsIntPoint(0, 0);
5165 int32_t x = 0, y = 0;
5166 aError = treeOwnerAsWin->GetPosition(&x, &y);
5167 return nsIntPoint(x, y);
5170 int32_t
5171 nsGlobalWindow::GetScreenX(ErrorResult& aError)
5173 FORWARD_TO_OUTER_OR_THROW(GetScreenX, (aError), aError, 0);
5175 return DevToCSSIntPixels(GetScreenXY(aError).x);
5178 NS_IMETHODIMP
5179 nsGlobalWindow::GetScreenX(int32_t* aScreenX)
5181 ErrorResult rv;
5182 *aScreenX = GetScreenX(rv);
5184 return rv.ErrorCode();
5187 nsRect
5188 nsGlobalWindow::GetInnerScreenRect()
5190 MOZ_ASSERT(IsOuterWindow());
5192 if (!mDocShell) {
5193 return nsRect();
5196 nsGlobalWindow* rootWindow =
5197 static_cast<nsGlobalWindow*>(GetPrivateRoot());
5198 if (rootWindow) {
5199 rootWindow->FlushPendingNotifications(Flush_Layout);
5202 if (!mDocShell) {
5203 return nsRect();
5206 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
5207 if (!presShell) {
5208 return nsRect();
5210 nsIFrame* rootFrame = presShell->GetRootFrame();
5211 if (!rootFrame) {
5212 return nsRect();
5215 return rootFrame->GetScreenRectInAppUnits();
5218 float
5219 nsGlobalWindow::GetMozInnerScreenX(ErrorResult& aError)
5221 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenX, (aError), aError, 0);
5223 nsRect r = GetInnerScreenRect();
5224 return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
5227 NS_IMETHODIMP
5228 nsGlobalWindow::GetMozInnerScreenX(float* aScreenX)
5230 ErrorResult rv;
5231 *aScreenX = GetMozInnerScreenX(rv);
5233 return rv.ErrorCode();
5236 float
5237 nsGlobalWindow::GetMozInnerScreenY(ErrorResult& aError)
5239 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenY, (aError), aError, 0);
5241 nsRect r = GetInnerScreenRect();
5242 return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
5245 NS_IMETHODIMP
5246 nsGlobalWindow::GetMozInnerScreenY(float* aScreenY)
5248 ErrorResult rv;
5249 *aScreenY = GetMozInnerScreenY(rv);
5251 return rv.ErrorCode();
5254 float
5255 nsGlobalWindow::GetDevicePixelRatio(ErrorResult& aError)
5257 FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatio, (aError), aError, 0.0);
5259 if (!mDocShell) {
5260 return 1.0;
5263 nsRefPtr<nsPresContext> presContext;
5264 mDocShell->GetPresContext(getter_AddRefs(presContext));
5265 if (!presContext) {
5266 return 1.0;
5269 return float(nsPresContext::AppUnitsPerCSSPixel())/
5270 presContext->AppUnitsPerDevPixel();
5273 NS_IMETHODIMP
5274 nsGlobalWindow::GetDevicePixelRatio(float* aRatio)
5276 ErrorResult rv;
5277 *aRatio = GetDevicePixelRatio(rv);
5279 return rv.ErrorCode();
5282 uint64_t
5283 nsGlobalWindow::GetMozPaintCount(ErrorResult& aError)
5285 FORWARD_TO_OUTER_OR_THROW(GetMozPaintCount, (aError), aError, 0);
5287 if (!mDocShell) {
5288 return 0;
5291 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
5292 return presShell ? presShell->GetPaintCount() : 0;
5295 NS_IMETHODIMP
5296 nsGlobalWindow::GetMozPaintCount(uint64_t* aResult)
5298 ErrorResult rv;
5299 *aResult = GetMozPaintCount(rv);
5301 return rv.ErrorCode();
5304 NS_IMETHODIMP
5305 nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback,
5306 int32_t *aHandle)
5308 if (!aCallback) {
5309 if (mDoc) {
5310 mDoc->WarnOnceAbout(nsIDocument::eMozBeforePaint);
5312 return NS_ERROR_XPC_BAD_CONVERT_JS;
5315 ErrorResult rv;
5316 nsIDocument::FrameRequestCallbackHolder holder(aCallback);
5317 *aHandle = RequestAnimationFrame(holder, rv);
5319 return rv.ErrorCode();
5322 int32_t
5323 nsGlobalWindow::RequestAnimationFrame(FrameRequestCallback& aCallback,
5324 ErrorResult& aError)
5326 nsIDocument::FrameRequestCallbackHolder holder(&aCallback);
5327 return RequestAnimationFrame(holder, aError);
5330 int32_t
5331 nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback,
5332 ErrorResult& aError)
5334 nsIDocument::FrameRequestCallbackHolder holder(aCallback);
5335 return RequestAnimationFrame(holder, aError);
5338 int32_t
5339 nsGlobalWindow::RequestAnimationFrame(const nsIDocument::FrameRequestCallbackHolder& aCallback,
5340 ErrorResult& aError)
5342 FORWARD_TO_INNER_OR_THROW(RequestAnimationFrame, (aCallback, aError), aError,
5345 if (!mDoc) {
5346 return 0;
5349 if (GetWrapperPreserveColor()) {
5350 js::NotifyAnimationActivity(GetWrapperPreserveColor());
5353 int32_t handle;
5354 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
5355 return handle;
5358 NS_IMETHODIMP
5359 nsGlobalWindow::RequestAnimationFrame(JS::Handle<JS::Value> aCallback,
5360 JSContext* cx,
5361 int32_t* aHandle)
5363 if (!aCallback.isObject() || !JS_ObjectIsCallable(cx, &aCallback.toObject())) {
5364 return NS_ERROR_INVALID_ARG;
5367 JS::Rooted<JSObject*> callbackObj(cx, &aCallback.toObject());
5368 nsRefPtr<FrameRequestCallback> callback =
5369 new FrameRequestCallback(callbackObj, GetIncumbentGlobal());
5371 ErrorResult rv;
5372 *aHandle = RequestAnimationFrame(*callback, rv);
5374 return rv.ErrorCode();
5377 NS_IMETHODIMP
5378 nsGlobalWindow::MozCancelRequestAnimationFrame(int32_t aHandle)
5380 return CancelAnimationFrame(aHandle);
5383 NS_IMETHODIMP
5384 nsGlobalWindow::MozCancelAnimationFrame(int32_t aHandle)
5386 return CancelAnimationFrame(aHandle);
5389 void
5390 nsGlobalWindow::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError)
5392 FORWARD_TO_INNER_OR_THROW(CancelAnimationFrame, (aHandle, aError), aError, );
5394 if (!mDoc) {
5395 return;
5398 mDoc->CancelFrameRequestCallback(aHandle);
5401 NS_IMETHODIMP
5402 nsGlobalWindow::CancelAnimationFrame(int32_t aHandle)
5404 ErrorResult rv;
5405 CancelAnimationFrame(aHandle, rv);
5407 return rv.ErrorCode();
5410 int64_t
5411 nsGlobalWindow::GetMozAnimationStartTime(ErrorResult& aError)
5413 FORWARD_TO_INNER_OR_THROW(GetMozAnimationStartTime, (aError), aError, 0);
5415 if (mDoc) {
5416 nsIPresShell* presShell = mDoc->GetShell();
5417 if (presShell) {
5418 return presShell->GetPresContext()->RefreshDriver()->
5419 MostRecentRefreshEpochTime() / PR_USEC_PER_MSEC;
5423 // If all else fails, just be compatible with Date.now()
5424 return JS_Now() / PR_USEC_PER_MSEC;
5427 NS_IMETHODIMP
5428 nsGlobalWindow::GetMozAnimationStartTime(int64_t *aTime)
5430 ErrorResult rv;
5431 *aTime = GetMozAnimationStartTime(rv);
5433 return rv.ErrorCode();
5436 already_AddRefed<MediaQueryList>
5437 nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
5438 ErrorResult& aError)
5440 // FIXME: This whole forward-to-outer and then get a pres
5441 // shell/context off the docshell dance is sort of silly; it'd make
5442 // more sense to forward to the inner, but it's what everyone else
5443 // (GetSelection, GetScrollXY, etc.) does around here.
5444 FORWARD_TO_OUTER_OR_THROW(MatchMedia, (aMediaQueryList, aError), aError,
5445 nullptr);
5447 // We need this now to ensure that we have a non-null |presContext|
5448 // when we ought to.
5449 // This is similar to EnsureSizeUpToDate, but only flushes frames.
5450 nsGlobalWindow *parent = static_cast<nsGlobalWindow*>(GetPrivateParent());
5451 if (parent) {
5452 parent->FlushPendingNotifications(Flush_Frames);
5455 if (!mDocShell) {
5456 return nullptr;
5459 nsRefPtr<nsPresContext> presContext;
5460 mDocShell->GetPresContext(getter_AddRefs(presContext));
5462 if (!presContext) {
5463 return nullptr;
5466 return presContext->MatchMedia(aMediaQueryList);
5469 NS_IMETHODIMP
5470 nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
5471 nsISupports** aResult)
5473 ErrorResult rv;
5474 nsRefPtr<MediaQueryList> mediaQueryList = MatchMedia(aMediaQueryList, rv);
5475 mediaQueryList.forget(aResult);
5477 return rv.ErrorCode();
5480 void
5481 nsGlobalWindow::SetScreenX(int32_t aScreenX, ErrorResult& aError)
5483 FORWARD_TO_OUTER_OR_THROW(SetScreenX, (aScreenX, aError), aError, );
5486 * If caller is not chrome and the user has not explicitly exempted the site,
5487 * prevent setting window.screenX by exiting early
5490 if (!CanMoveResizeWindows() || IsFrame()) {
5491 return;
5494 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5495 if (!treeOwnerAsWin) {
5496 aError.Throw(NS_ERROR_FAILURE);
5497 return;
5500 int32_t x, y;
5501 aError = treeOwnerAsWin->GetPosition(&x, &y);
5502 if (aError.Failed()) {
5503 return;
5506 CheckSecurityLeftAndTop(&aScreenX, nullptr);
5507 x = CSSToDevIntPixels(aScreenX);
5509 aError = treeOwnerAsWin->SetPosition(x, y);
5512 NS_IMETHODIMP
5513 nsGlobalWindow::SetScreenX(int32_t aScreenX)
5515 ErrorResult rv;
5516 SetScreenX(aScreenX, rv);
5518 return rv.ErrorCode();
5521 int32_t
5522 nsGlobalWindow::GetScreenY(ErrorResult& aError)
5524 FORWARD_TO_OUTER_OR_THROW(GetScreenY, (aError), aError, 0);
5526 return DevToCSSIntPixels(GetScreenXY(aError).y);
5529 NS_IMETHODIMP
5530 nsGlobalWindow::GetScreenY(int32_t* aScreenY)
5532 ErrorResult rv;
5533 *aScreenY = GetScreenY(rv);
5535 return rv.ErrorCode();
5538 void
5539 nsGlobalWindow::SetScreenY(int32_t aScreenY, ErrorResult& aError)
5541 FORWARD_TO_OUTER_OR_THROW(SetScreenY, (aScreenY, aError), aError, );
5544 * If caller is not chrome and the user has not explicitly exempted the site,
5545 * prevent setting window.screenY by exiting early
5548 if (!CanMoveResizeWindows() || IsFrame()) {
5549 return;
5552 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5553 if (!treeOwnerAsWin) {
5554 aError.Throw(NS_ERROR_FAILURE);
5555 return;
5558 int32_t x, y;
5559 aError = treeOwnerAsWin->GetPosition(&x, &y);
5560 if (aError.Failed()) {
5561 return;
5564 CheckSecurityLeftAndTop(nullptr, &aScreenY);
5565 y = CSSToDevIntPixels(aScreenY);
5567 aError = treeOwnerAsWin->SetPosition(x, y);
5570 NS_IMETHODIMP
5571 nsGlobalWindow::SetScreenY(int32_t aScreenY)
5573 ErrorResult rv;
5574 SetScreenY(aScreenY, rv);
5576 return rv.ErrorCode();
5579 // NOTE: Arguments to this function should have values scaled to
5580 // CSS pixels, not device pixels.
5581 void
5582 nsGlobalWindow::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight)
5584 MOZ_ASSERT(IsOuterWindow());
5586 #ifdef MOZ_XUL
5587 if (!nsContentUtils::IsCallerChrome()) {
5588 // if attempting to resize the window, hide any open popups
5589 nsContentUtils::HidePopupsInDocument(mDoc);
5591 #endif
5593 // This one is easy. Just ensure the variable is greater than 100;
5594 if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
5595 // Check security state for use in determing window dimensions
5597 if (!nsContentUtils::IsCallerChrome()) {
5598 //sec check failed
5599 if (aWidth && *aWidth < 100) {
5600 *aWidth = 100;
5602 if (aHeight && *aHeight < 100) {
5603 *aHeight = 100;
5609 // NOTE: Arguments to this function should have values in device pixels
5610 nsresult
5611 nsGlobalWindow::SetDocShellWidthAndHeight(int32_t aInnerWidth, int32_t aInnerHeight)
5613 MOZ_ASSERT(IsOuterWindow());
5615 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
5617 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
5618 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
5619 NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
5621 NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(mDocShell, aInnerWidth, aInnerHeight),
5622 NS_ERROR_FAILURE);
5624 return NS_OK;
5627 // NOTE: Arguments to this function should have values in app units
5628 void
5629 nsGlobalWindow::SetCSSViewportWidthAndHeight(nscoord aInnerWidth, nscoord aInnerHeight)
5631 MOZ_ASSERT(IsOuterWindow());
5633 nsRefPtr<nsPresContext> presContext;
5634 mDocShell->GetPresContext(getter_AddRefs(presContext));
5636 nsRect shellArea = presContext->GetVisibleArea();
5637 shellArea.height = aInnerHeight;
5638 shellArea.width = aInnerWidth;
5640 presContext->SetVisibleArea(shellArea);
5643 // NOTE: Arguments to this function should have values scaled to
5644 // CSS pixels, not device pixels.
5645 void
5646 nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop)
5648 MOZ_ASSERT(IsOuterWindow());
5650 // This one is harder. We have to get the screen size and window dimensions.
5652 // Check security state for use in determing window dimensions
5654 if (!nsContentUtils::IsCallerChrome()) {
5655 #ifdef MOZ_XUL
5656 // if attempting to move the window, hide any open popups
5657 nsContentUtils::HidePopupsInDocument(mDoc);
5658 #endif
5660 nsGlobalWindow* rootWindow =
5661 static_cast<nsGlobalWindow*>(GetPrivateRoot());
5662 if (rootWindow) {
5663 rootWindow->FlushPendingNotifications(Flush_Layout);
5666 nsCOMPtr<nsIBaseWindow> treeOwner = GetTreeOwnerWindow();
5668 nsCOMPtr<nsIDOMScreen> screen;
5669 GetScreen(getter_AddRefs(screen));
5671 if (treeOwner && screen) {
5672 int32_t screenLeft, screenTop, screenWidth, screenHeight;
5673 int32_t winLeft, winTop, winWidth, winHeight;
5675 // Get the window size
5676 treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
5678 // convert those values to CSS pixels
5679 // XXX four separate retrievals of the prescontext
5680 winLeft = DevToCSSIntPixels(winLeft);
5681 winTop = DevToCSSIntPixels(winTop);
5682 winWidth = DevToCSSIntPixels(winWidth);
5683 winHeight = DevToCSSIntPixels(winHeight);
5685 // Get the screen dimensions
5686 // XXX This should use nsIScreenManager once it's fully fleshed out.
5687 screen->GetAvailLeft(&screenLeft);
5688 screen->GetAvailWidth(&screenWidth);
5689 screen->GetAvailHeight(&screenHeight);
5690 #if defined(XP_MACOSX)
5691 /* The mac's coordinate system is different from the assumed Windows'
5692 system. It offsets by the height of the menubar so that a window
5693 placed at (0,0) will be entirely visible. Unfortunately that
5694 correction is made elsewhere (in Widget) and the meaning of
5695 the Avail... coordinates is overloaded. Here we allow a window
5696 to be placed at (0,0) because it does make sense to do so.
5698 screen->GetTop(&screenTop);
5699 #else
5700 screen->GetAvailTop(&screenTop);
5701 #endif
5703 if (aLeft) {
5704 if (screenLeft+screenWidth < *aLeft+winWidth)
5705 *aLeft = screenLeft+screenWidth - winWidth;
5706 if (screenLeft > *aLeft)
5707 *aLeft = screenLeft;
5709 if (aTop) {
5710 if (screenTop+screenHeight < *aTop+winHeight)
5711 *aTop = screenTop+screenHeight - winHeight;
5712 if (screenTop > *aTop)
5713 *aTop = screenTop;
5715 } else {
5716 if (aLeft)
5717 *aLeft = 0;
5718 if (aTop)
5719 *aTop = 0;
5724 NS_IMETHODIMP
5725 nsGlobalWindow::GetPageXOffset(int32_t* aPageXOffset)
5727 return GetScrollX(aPageXOffset);
5730 NS_IMETHODIMP
5731 nsGlobalWindow::GetPageYOffset(int32_t* aPageYOffset)
5733 return GetScrollY(aPageYOffset);
5736 void
5737 nsGlobalWindow::GetScrollMaxXY(int32_t* aScrollMaxX, int32_t* aScrollMaxY,
5738 ErrorResult& aError)
5740 FORWARD_TO_OUTER_OR_THROW(GetScrollMaxXY, (aScrollMaxX, aScrollMaxY, aError),
5741 aError, );
5743 FlushPendingNotifications(Flush_Layout);
5744 nsIScrollableFrame *sf = GetScrollFrame();
5745 if (!sf) {
5746 return;
5749 nsRect scrollRange = sf->GetScrollRange();
5751 if (aScrollMaxX) {
5752 *aScrollMaxX = std::max(0,
5753 (int32_t)floor(nsPresContext::AppUnitsToFloatCSSPixels(scrollRange.XMost())));
5755 if (aScrollMaxY) {
5756 *aScrollMaxY = std::max(0,
5757 (int32_t)floor(nsPresContext::AppUnitsToFloatCSSPixels(scrollRange.YMost())));
5761 int32_t
5762 nsGlobalWindow::GetScrollMaxX(ErrorResult& aError)
5764 int32_t scrollMaxX = 0;
5765 GetScrollMaxXY(&scrollMaxX, nullptr, aError);
5766 return scrollMaxX;
5769 NS_IMETHODIMP
5770 nsGlobalWindow::GetScrollMaxX(int32_t* aScrollMaxX)
5772 NS_ENSURE_ARG_POINTER(aScrollMaxX);
5773 ErrorResult rv;
5774 *aScrollMaxX = GetScrollMaxX(rv);
5776 return rv.ErrorCode();
5779 int32_t
5780 nsGlobalWindow::GetScrollMaxY(ErrorResult& aError)
5782 int32_t scrollMaxY = 0;
5783 GetScrollMaxXY(nullptr, &scrollMaxY, aError);
5784 return scrollMaxY;
5787 NS_IMETHODIMP
5788 nsGlobalWindow::GetScrollMaxY(int32_t* aScrollMaxY)
5790 NS_ENSURE_ARG_POINTER(aScrollMaxY);
5791 ErrorResult rv;
5792 *aScrollMaxY = GetScrollMaxY(rv);
5794 return rv.ErrorCode();
5797 CSSIntPoint
5798 nsGlobalWindow::GetScrollXY(bool aDoFlush)
5800 MOZ_ASSERT(IsOuterWindow());
5802 if (aDoFlush) {
5803 FlushPendingNotifications(Flush_Layout);
5804 } else {
5805 EnsureSizeUpToDate();
5808 nsIScrollableFrame *sf = GetScrollFrame();
5809 if (!sf) {
5810 return CSSIntPoint(0, 0);
5813 nsPoint scrollPos = sf->GetScrollPosition();
5814 if (scrollPos != nsPoint(0,0) && !aDoFlush) {
5815 // Oh, well. This is the expensive case -- the window is scrolled and we
5816 // didn't actually flush yet. Repeat, but with a flush, since the content
5817 // may get shorter and hence our scroll position may decrease.
5818 return GetScrollXY(true);
5821 return sf->GetScrollPositionCSSPixels();
5824 int32_t
5825 nsGlobalWindow::GetScrollX(ErrorResult& aError)
5827 FORWARD_TO_OUTER_OR_THROW(GetScrollX, (aError), aError, 0);
5828 return GetScrollXY(false).x;
5831 NS_IMETHODIMP
5832 nsGlobalWindow::GetScrollX(int32_t* aScrollX)
5834 NS_ENSURE_ARG_POINTER(aScrollX);
5835 ErrorResult rv;
5836 *aScrollX = GetScrollX(rv);
5837 return rv.ErrorCode();
5840 int32_t
5841 nsGlobalWindow::GetScrollY(ErrorResult& aError)
5843 FORWARD_TO_OUTER_OR_THROW(GetScrollY, (aError), aError, 0);
5844 return GetScrollXY(false).y;
5847 NS_IMETHODIMP
5848 nsGlobalWindow::GetScrollY(int32_t* aScrollY)
5850 NS_ENSURE_ARG_POINTER(aScrollY);
5851 ErrorResult rv;
5852 *aScrollY = GetScrollY(rv);
5853 return rv.ErrorCode();
5856 uint32_t
5857 nsGlobalWindow::Length()
5859 FORWARD_TO_OUTER(Length, (), 0);
5861 nsDOMWindowList* windows = GetWindowList();
5863 return windows ? windows->GetLength() : 0;
5866 NS_IMETHODIMP
5867 nsGlobalWindow::GetLength(uint32_t* aLength)
5869 *aLength = Length();
5870 return NS_OK;
5873 nsPIDOMWindow*
5874 nsGlobalWindow::GetChildWindow(const nsAString& aName)
5876 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
5877 NS_ENSURE_TRUE(docShell, nullptr);
5879 nsCOMPtr<nsIDocShellTreeItem> child;
5880 docShell->FindChildWithName(PromiseFlatString(aName).get(),
5881 false, true, nullptr, nullptr,
5882 getter_AddRefs(child));
5884 return child ? child->GetWindow() : nullptr;
5887 bool
5888 nsGlobalWindow::DispatchCustomEvent(const nsAString& aEventName)
5890 MOZ_ASSERT(IsOuterWindow());
5892 bool defaultActionEnabled = true;
5893 nsContentUtils::DispatchTrustedEvent(mDoc, ToSupports(this), aEventName,
5894 true, true, &defaultActionEnabled);
5896 return defaultActionEnabled;
5899 bool
5900 nsGlobalWindow::DispatchResizeEvent(const CSSIntSize& aSize)
5902 MOZ_ASSERT(IsOuterWindow());
5904 ErrorResult res;
5905 nsRefPtr<Event> domEvent =
5906 mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res);
5907 if (res.Failed()) {
5908 return false;
5911 AutoSafeJSContext cx;
5912 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
5913 DOMWindowResizeEventDetail detail;
5914 detail.mWidth = aSize.width;
5915 detail.mHeight = aSize.height;
5916 JS::Rooted<JS::Value> detailValue(cx);
5917 if (!ToJSValue(cx, detail, &detailValue)) {
5918 return false;
5921 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
5922 customEvent->InitCustomEvent(cx,
5923 NS_LITERAL_STRING("DOMWindowResize"),
5924 /* bubbles = */ true,
5925 /* cancelable = */ true,
5926 detailValue,
5927 res);
5928 if (res.Failed()) {
5929 return false;
5932 domEvent->SetTrusted(true);
5933 domEvent->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
5935 nsCOMPtr<EventTarget> target = do_QueryInterface(GetOuterWindow());
5936 domEvent->SetTarget(target);
5938 bool defaultActionEnabled = true;
5939 target->DispatchEvent(domEvent, &defaultActionEnabled);
5941 return defaultActionEnabled;
5944 void
5945 nsGlobalWindow::RefreshCompartmentPrincipal()
5947 MOZ_ASSERT(IsInnerWindow());
5949 JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
5950 nsJSPrincipals::get(mDoc->NodePrincipal()));
5953 static already_AddRefed<nsIDocShellTreeItem>
5954 GetCallerDocShellTreeItem()
5956 nsCOMPtr<nsIWebNavigation> callerWebNav = do_GetInterface(GetEntryGlobal());
5957 nsCOMPtr<nsIDocShellTreeItem> callerItem = do_QueryInterface(callerWebNav);
5959 return callerItem.forget();
5962 bool
5963 nsGlobalWindow::WindowExists(const nsAString& aName,
5964 bool aLookForCallerOnJSStack)
5966 NS_PRECONDITION(IsOuterWindow(), "Must be outer window");
5967 NS_PRECONDITION(mDocShell, "Must have docshell");
5969 nsCOMPtr<nsIDocShellTreeItem> caller;
5970 if (aLookForCallerOnJSStack) {
5971 caller = GetCallerDocShellTreeItem();
5974 if (!caller) {
5975 caller = mDocShell;
5978 nsCOMPtr<nsIDocShellTreeItem> namedItem;
5979 mDocShell->FindItemWithName(PromiseFlatString(aName).get(), nullptr, caller,
5980 getter_AddRefs(namedItem));
5981 return namedItem != nullptr;
5984 already_AddRefed<nsIWidget>
5985 nsGlobalWindow::GetMainWidget()
5987 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
5989 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5991 nsCOMPtr<nsIWidget> widget;
5993 if (treeOwnerAsWin) {
5994 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
5997 return widget.forget();
6000 nsIWidget*
6001 nsGlobalWindow::GetNearestWidget()
6003 nsIDocShell* docShell = GetDocShell();
6004 NS_ENSURE_TRUE(docShell, nullptr);
6005 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
6006 NS_ENSURE_TRUE(presShell, nullptr);
6007 nsIFrame* rootFrame = presShell->GetRootFrame();
6008 NS_ENSURE_TRUE(rootFrame, nullptr);
6009 return rootFrame->GetView()->GetNearestWidget(nullptr);
6012 void
6013 nsGlobalWindow::SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError)
6015 FORWARD_TO_OUTER_OR_THROW(SetFullScreen, (aFullScreen, aError), aError, /* void */);
6017 aError = SetFullScreenInternal(aFullScreen, true);
6020 NS_IMETHODIMP
6021 nsGlobalWindow::SetFullScreen(bool aFullScreen)
6023 FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);
6025 return SetFullScreenInternal(aFullScreen, true);
6028 nsresult
6029 nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
6031 MOZ_ASSERT(IsOuterWindow());
6033 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
6035 // Only chrome can change our fullScreen mode, unless we're running in
6036 // untrusted mode.
6037 if (aFullScreen == FullScreen() ||
6038 (aRequireTrust && !nsContentUtils::IsCallerChrome())) {
6039 return NS_OK;
6042 // SetFullScreen needs to be called on the root window, so get that
6043 // via the DocShell tree, and if we are not already the root,
6044 // call SetFullScreen on that window instead.
6045 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6046 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6047 nsCOMPtr<nsPIDOMWindow> window = rootItem ? rootItem->GetWindow() : nullptr;
6048 if (!window)
6049 return NS_ERROR_FAILURE;
6050 if (rootItem != mDocShell)
6051 return window->SetFullScreenInternal(aFullScreen, aRequireTrust);
6053 // make sure we don't try to set full screen on a non-chrome window,
6054 // which might happen in embedding world
6055 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome)
6056 return NS_ERROR_FAILURE;
6058 // If we are already in full screen mode, just return.
6059 if (mFullScreen == aFullScreen)
6060 return NS_OK;
6062 // dispatch a "fullscreen" DOM event so that XUL apps can
6063 // respond visually if we are kicked into full screen mode
6064 if (!DispatchCustomEvent(NS_LITERAL_STRING("fullscreen"))) {
6065 return NS_OK;
6068 // Prevent chrome documents which are still loading from resizing
6069 // the window after we set fullscreen mode.
6070 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6071 nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwnerAsWin));
6072 if (aFullScreen && xulWin) {
6073 xulWin->SetIntrinsicallySized(false);
6076 // Set this before so if widget sends an event indicating its
6077 // gone full screen, the state trap above works.
6078 mFullScreen = aFullScreen;
6080 // Sometimes we don't want the top-level widget to actually go fullscreen,
6081 // for example in the B2G desktop client, we don't want the emulated screen
6082 // dimensions to appear to increase when entering fullscreen mode; we just
6083 // want the content to fill the entire client area of the emulator window.
6084 if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) {
6085 nsCOMPtr<nsIWidget> widget = GetMainWidget();
6086 if (widget)
6087 widget->MakeFullScreen(aFullScreen);
6090 if (!mFullScreen) {
6091 // Force exit from DOM full-screen mode. This is so that if we're in
6092 // DOM full-screen mode and the user exits full-screen mode with
6093 // the browser full-screen mode toggle keyboard-shortcut, we'll detect
6094 // that and leave DOM API full-screen mode too.
6095 nsIDocument::ExitFullscreen(mDoc, /* async */ false);
6098 if (!mWakeLock && mFullScreen) {
6099 nsRefPtr<power::PowerManagerService> pmService =
6100 power::PowerManagerService::GetInstance();
6101 NS_ENSURE_TRUE(pmService, NS_OK);
6103 ErrorResult rv;
6104 mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"),
6105 this, rv);
6106 if (rv.Failed()) {
6107 return rv.ErrorCode();
6110 } else if (mWakeLock && !mFullScreen) {
6111 ErrorResult rv;
6112 mWakeLock->Unlock(rv);
6113 NS_WARN_IF_FALSE(!rv.Failed(), "Failed to unlock the wakelock.");
6114 mWakeLock = nullptr;
6117 return NS_OK;
6120 bool
6121 nsGlobalWindow::FullScreen() const
6123 MOZ_ASSERT(IsOuterWindow());
6125 NS_ENSURE_TRUE(mDocShell, mFullScreen);
6127 // Get the fullscreen value of the root window, to always have the value
6128 // accurate, even when called from content.
6129 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6130 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6131 if (rootItem == mDocShell) {
6132 // We are the root window. Return our internal value.
6133 return mFullScreen;
6136 nsCOMPtr<nsIDOMWindow> window = rootItem->GetWindow();
6137 NS_ENSURE_TRUE(window, mFullScreen);
6139 return static_cast<nsGlobalWindow*>(window.get())->FullScreen();
6142 bool
6143 nsGlobalWindow::GetFullScreen(ErrorResult& aError)
6145 FORWARD_TO_OUTER_OR_THROW(GetFullScreen, (aError), aError, false);
6146 return FullScreen();
6149 NS_IMETHODIMP
6150 nsGlobalWindow::GetFullScreen(bool* aFullScreen)
6152 ErrorResult rv;
6153 *aFullScreen = GetFullScreen(rv);
6155 return rv.ErrorCode();
6158 NS_IMETHODIMP
6159 nsGlobalWindow::Dump(const nsAString& aStr)
6161 if (!nsContentUtils::DOMWindowDumpEnabled()) {
6162 return NS_OK;
6165 char *cstr = ToNewUTF8String(aStr);
6167 #if defined(XP_MACOSX)
6168 // have to convert \r to \n so that printing to the console works
6169 char *c = cstr, *cEnd = cstr + strlen(cstr);
6170 while (c < cEnd) {
6171 if (*c == '\r')
6172 *c = '\n';
6173 c++;
6175 #endif
6177 if (cstr) {
6178 #ifdef XP_WIN
6179 PrintToDebugger(cstr);
6180 #endif
6181 #ifdef ANDROID
6182 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
6183 #endif
6184 FILE *fp = gDumpFile ? gDumpFile : stdout;
6185 fputs(cstr, fp);
6186 fflush(fp);
6187 nsMemory::Free(cstr);
6190 return NS_OK;
6193 void
6194 nsGlobalWindow::EnsureReflowFlushAndPaint()
6196 MOZ_ASSERT(IsOuterWindow());
6197 NS_ASSERTION(mDocShell, "EnsureReflowFlushAndPaint() called with no "
6198 "docshell!");
6200 if (!mDocShell)
6201 return;
6203 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
6205 if (!presShell)
6206 return;
6208 // Flush pending reflows.
6209 if (mDoc) {
6210 mDoc->FlushPendingNotifications(Flush_Layout);
6213 // Unsuppress painting.
6214 presShell->UnsuppressPainting();
6217 NS_IMETHODIMP
6218 nsGlobalWindow::GetTextZoom(float *aZoom)
6220 FORWARD_TO_OUTER(GetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED);
6222 if (mDocShell) {
6223 nsCOMPtr<nsIContentViewer> contentViewer;
6224 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
6226 if (contentViewer) {
6227 return contentViewer->GetTextZoom(aZoom);
6230 return NS_ERROR_FAILURE;
6233 NS_IMETHODIMP
6234 nsGlobalWindow::SetTextZoom(float aZoom)
6236 FORWARD_TO_OUTER(SetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED);
6238 if (mDocShell) {
6239 nsCOMPtr<nsIContentViewer> contentViewer;
6240 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
6242 if (contentViewer)
6243 return contentViewer->SetTextZoom(aZoom);
6245 return NS_ERROR_FAILURE;
6248 // static
6249 void
6250 nsGlobalWindow::MakeScriptDialogTitle(nsAString &aOutTitle)
6252 aOutTitle.Truncate();
6254 // Try to get a host from the running principal -- this will do the
6255 // right thing for javascript: and data: documents.
6257 nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
6258 nsCOMPtr<nsIURI> uri;
6259 nsresult rv = principal->GetURI(getter_AddRefs(uri));
6260 // Note - The check for the current JSContext here isn't necessarily sensical.
6261 // It's just designed to preserve existing behavior during a mass-conversion
6262 // patch.
6263 if (NS_SUCCEEDED(rv) && uri && nsContentUtils::GetCurrentJSContext()) {
6264 // remove user:pass for privacy and spoof prevention
6266 nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
6267 if (fixup) {
6268 nsCOMPtr<nsIURI> fixedURI;
6269 rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
6270 if (NS_SUCCEEDED(rv) && fixedURI) {
6271 nsAutoCString host;
6272 fixedURI->GetHost(host);
6274 if (!host.IsEmpty()) {
6275 // if this URI has a host we'll show it. For other
6276 // schemes (e.g. file:) we fall back to the localized
6277 // generic string
6279 nsAutoCString prepath;
6280 fixedURI->GetPrePath(prepath);
6282 NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
6283 const char16_t *formatStrings[] = { ucsPrePath.get() };
6284 nsXPIDLString tempString;
6285 nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6286 "ScriptDlgHeading",
6287 formatStrings,
6288 tempString);
6289 aOutTitle = tempString;
6295 if (aOutTitle.IsEmpty()) {
6296 // We didn't find a host so use the generic heading
6297 nsXPIDLString tempString;
6298 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6299 "ScriptDlgGenericHeading",
6300 tempString);
6301 aOutTitle = tempString;
6304 // Just in case
6305 if (aOutTitle.IsEmpty()) {
6306 NS_WARNING("could not get ScriptDlgGenericHeading string from string bundle");
6307 aOutTitle.AssignLiteral("[Script]");
6311 bool
6312 nsGlobalWindow::CanMoveResizeWindows()
6314 MOZ_ASSERT(IsOuterWindow());
6316 // When called from chrome, we can avoid the following checks.
6317 if (!nsContentUtils::IsCallerChrome()) {
6318 // Don't allow scripts to move or resize windows that were not opened by a
6319 // script.
6320 if (!mHadOriginalOpener) {
6321 return false;
6324 if (!CanSetProperty("dom.disable_window_move_resize")) {
6325 return false;
6328 // Ignore the request if we have more than one tab in the window.
6329 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6330 if (treeOwner) {
6331 uint32_t itemCount;
6332 if (NS_SUCCEEDED(treeOwner->GetTargetableShellCount(&itemCount)) &&
6333 itemCount > 1) {
6334 return false;
6339 // The preference is useful for the webapp runtime. Webapps should be able
6340 // to resize or move their window.
6341 if (mDocShell && !Preferences::GetBool("dom.always_allow_move_resize_window",
6342 false)) {
6343 bool allow;
6344 nsresult rv = mDocShell->GetAllowWindowControl(&allow);
6345 if (NS_SUCCEEDED(rv) && !allow)
6346 return false;
6349 if (gMouseDown && !gDragServiceDisabled) {
6350 nsCOMPtr<nsIDragService> ds =
6351 do_GetService("@mozilla.org/widget/dragservice;1");
6352 if (ds) {
6353 gDragServiceDisabled = true;
6354 ds->Suppress();
6357 return true;
6360 bool
6361 nsGlobalWindow::AlertOrConfirm(bool aAlert,
6362 const nsAString& aMessage,
6363 mozilla::ErrorResult& aError)
6365 // XXX This method is very similar to nsGlobalWindow::Prompt, make
6366 // sure any modifications here don't need to happen over there!
6367 MOZ_ASSERT(IsOuterWindow());
6369 if (!AreDialogsEnabled()) {
6370 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6371 return false;
6374 // Reset popup state while opening a modal dialog, and firing events
6375 // about the dialog, to prevent the current state from being active
6376 // the whole time a modal dialog is open.
6377 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
6379 // Before bringing up the window, unsuppress painting and flush
6380 // pending reflows.
6381 EnsureReflowFlushAndPaint();
6383 nsAutoString title;
6384 MakeScriptDialogTitle(title);
6386 // Remove non-terminating null characters from the
6387 // string. See bug #310037.
6388 nsAutoString final;
6389 nsContentUtils::StripNullChars(aMessage, final);
6391 nsresult rv;
6392 nsCOMPtr<nsIPromptFactory> promptFac =
6393 do_GetService("@mozilla.org/prompter;1", &rv);
6394 if (NS_FAILED(rv)) {
6395 aError.Throw(rv);
6396 return false;
6399 nsCOMPtr<nsIPrompt> prompt;
6400 aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
6401 getter_AddRefs(prompt));
6402 if (aError.Failed()) {
6403 return false;
6406 // Always allow tab modal prompts for alert and confirm.
6407 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
6408 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
6411 bool result = false;
6412 nsAutoSyncOperation sync(mDoc);
6413 if (ShouldPromptToBlockDialogs()) {
6414 bool disallowDialog = false;
6415 nsXPIDLString label;
6416 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6417 "ScriptDialogLabel", label);
6419 aError = aAlert ?
6420 prompt->AlertCheck(title.get(), final.get(), label.get(),
6421 &disallowDialog) :
6422 prompt->ConfirmCheck(title.get(), final.get(), label.get(),
6423 &disallowDialog, &result);
6425 if (disallowDialog)
6426 DisableDialogs();
6427 } else {
6428 aError = aAlert ?
6429 prompt->Alert(title.get(), final.get()) :
6430 prompt->Confirm(title.get(), final.get(), &result);
6433 return result;
6436 void
6437 nsGlobalWindow::Alert(mozilla::ErrorResult& aError)
6439 Alert(EmptyString(), aError);
6442 void
6443 nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError)
6445 FORWARD_TO_OUTER_OR_THROW(Alert, (aMessage, aError), aError, );
6446 AlertOrConfirm(/* aAlert = */ true, aMessage, aError);
6449 NS_IMETHODIMP
6450 nsGlobalWindow::Alert(const nsAString& aString)
6452 ErrorResult rv;
6453 Alert(aString, rv);
6455 return rv.ErrorCode();
6458 bool
6459 nsGlobalWindow::Confirm(const nsAString& aMessage, ErrorResult& aError)
6461 FORWARD_TO_OUTER_OR_THROW(Confirm, (aMessage, aError), aError, false);
6463 return AlertOrConfirm(/* aAlert = */ false, aMessage, aError);
6466 NS_IMETHODIMP
6467 nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
6469 ErrorResult rv;
6470 *aReturn = Confirm(aString, rv);
6472 return rv.ErrorCode();
6475 void
6476 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
6477 nsAString& aReturn, ErrorResult& aError)
6479 // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
6480 // sure any modifications here don't need to happen over there!
6481 FORWARD_TO_OUTER_OR_THROW(Prompt, (aMessage, aInitial, aReturn, aError),
6482 aError, );
6484 SetDOMStringToNull(aReturn);
6486 if (!AreDialogsEnabled()) {
6487 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6488 return;
6491 // Reset popup state while opening a modal dialog, and firing events
6492 // about the dialog, to prevent the current state from being active
6493 // the whole time a modal dialog is open.
6494 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
6496 // Before bringing up the window, unsuppress painting and flush
6497 // pending reflows.
6498 EnsureReflowFlushAndPaint();
6500 nsAutoString title;
6501 MakeScriptDialogTitle(title);
6503 // Remove non-terminating null characters from the
6504 // string. See bug #310037.
6505 nsAutoString fixedMessage, fixedInitial;
6506 nsContentUtils::StripNullChars(aMessage, fixedMessage);
6507 nsContentUtils::StripNullChars(aInitial, fixedInitial);
6509 nsresult rv;
6510 nsCOMPtr<nsIPromptFactory> promptFac =
6511 do_GetService("@mozilla.org/prompter;1", &rv);
6512 if (NS_FAILED(rv)) {
6513 aError.Throw(rv);
6514 return;
6517 nsCOMPtr<nsIPrompt> prompt;
6518 aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
6519 getter_AddRefs(prompt));
6520 if (aError.Failed()) {
6521 return;
6524 // Always allow tab modal prompts for prompt.
6525 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
6526 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
6529 // Pass in the default value, if any.
6530 char16_t *inoutValue = ToNewUnicode(fixedInitial);
6531 bool disallowDialog = false;
6533 nsXPIDLString label;
6534 if (ShouldPromptToBlockDialogs()) {
6535 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6536 "ScriptDialogLabel", label);
6539 nsAutoSyncOperation sync(mDoc);
6540 bool ok;
6541 aError = prompt->Prompt(title.get(), fixedMessage.get(),
6542 &inoutValue, label.get(), &disallowDialog, &ok);
6544 if (disallowDialog) {
6545 DisableDialogs();
6548 if (aError.Failed()) {
6549 return;
6552 nsAdoptingString outValue(inoutValue);
6554 if (ok && outValue) {
6555 aReturn.Assign(outValue);
6559 NS_IMETHODIMP
6560 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
6561 nsAString& aReturn)
6563 ErrorResult rv;
6564 Prompt(aMessage, aInitial, aReturn, rv);
6566 return rv.ErrorCode();
6569 void
6570 nsGlobalWindow::Focus(ErrorResult& aError)
6572 FORWARD_TO_OUTER_OR_THROW(Focus, (aError), aError, );
6574 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6575 if (!fm) {
6576 return;
6579 nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
6581 bool isVisible = false;
6582 if (baseWin) {
6583 baseWin->GetVisibility(&isVisible);
6586 if (!isVisible) {
6587 // A hidden tab is being focused, ignore this call.
6588 return;
6591 nsCOMPtr<nsPIDOMWindow> caller = do_QueryInterface(GetEntryGlobal());
6592 caller = caller ? caller->GetOuterWindow() : nullptr;
6593 nsCOMPtr<nsIDOMWindow> opener;
6594 GetOpener(getter_AddRefs(opener));
6596 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
6597 // window which opened us to raise us at times when popups are allowed
6598 // (bugs 355482 and 369306).
6599 bool canFocus = CanSetProperty("dom.disable_window_flip") ||
6600 (opener == caller &&
6601 RevisePopupAbuseLevel(gPopupControlState) < openAbused);
6603 nsCOMPtr<nsIDOMWindow> activeWindow;
6604 fm->GetActiveWindow(getter_AddRefs(activeWindow));
6606 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6607 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6608 nsCOMPtr<nsIDOMWindow> rootWin = rootItem ? rootItem->GetWindow() : nullptr;
6609 bool isActive = (rootWin == activeWindow);
6611 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6612 if (treeOwnerAsWin && (canFocus || isActive)) {
6613 bool isEnabled = true;
6614 if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
6615 NS_WARNING( "Should not try to set the focus on a disabled window" );
6616 return;
6619 // XXXndeakin not sure what this is for or if it should go somewhere else
6620 nsCOMPtr<nsIEmbeddingSiteWindow> embeddingWin(do_GetInterface(treeOwnerAsWin));
6621 if (embeddingWin)
6622 embeddingWin->SetFocus();
6625 if (!mDocShell) {
6626 return;
6629 nsCOMPtr<nsIPresShell> presShell;
6630 // Don't look for a presshell if we're a root chrome window that's got
6631 // about:blank loaded. We don't want to focus our widget in that case.
6632 // XXXbz should we really be checking for IsInitialDocument() instead?
6633 bool lookForPresShell = true;
6634 if (mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome &&
6635 GetPrivateRoot() == static_cast<nsIDOMWindow*>(this) &&
6636 mDoc) {
6637 nsIURI* ourURI = mDoc->GetDocumentURI();
6638 if (ourURI) {
6639 lookForPresShell = !NS_IsAboutBlank(ourURI);
6643 if (lookForPresShell) {
6644 mDocShell->GetEldestPresShell(getter_AddRefs(presShell));
6647 nsCOMPtr<nsIDocShellTreeItem> parentDsti;
6648 mDocShell->GetParent(getter_AddRefs(parentDsti));
6650 // set the parent's current focus to the frame containing this window.
6651 nsCOMPtr<nsPIDOMWindow> parent =
6652 parentDsti ? parentDsti->GetWindow() : nullptr;
6653 if (parent) {
6654 nsCOMPtr<nsIDocument> parentdoc = parent->GetDoc();
6655 if (!parentdoc) {
6656 return;
6659 nsIContent* frame = parentdoc->FindContentForSubDocument(mDoc);
6660 nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(frame);
6661 if (frameElement) {
6662 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
6663 if (canFocus)
6664 flags |= nsIFocusManager::FLAG_RAISE;
6665 aError = fm->SetFocus(frameElement, flags);
6667 return;
6669 if (nsCOMPtr<nsITabChild> child = do_GetInterface(mDocShell)) {
6670 child->SendRequestFocus(canFocus);
6671 return;
6673 if (canFocus) {
6674 // if there is no parent, this must be a toplevel window, so raise the
6675 // window if canFocus is true
6676 aError = fm->SetActiveWindow(this);
6680 NS_IMETHODIMP
6681 nsGlobalWindow::Focus()
6683 ErrorResult rv;
6684 Focus(rv);
6686 return rv.ErrorCode();
6689 void
6690 nsGlobalWindow::Blur(ErrorResult& aError)
6692 FORWARD_TO_OUTER_OR_THROW(Blur, (aError), aError, );
6694 // If dom.disable_window_flip == true, then content should not be allowed
6695 // to call this function (this would allow popunders, bug 369306)
6696 if (!CanSetProperty("dom.disable_window_flip")) {
6697 return;
6700 // If embedding apps don't implement nsIEmbeddingSiteWindow, we
6701 // shouldn't throw exceptions to web content.
6703 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6704 nsCOMPtr<nsIEmbeddingSiteWindow> siteWindow(do_GetInterface(treeOwner));
6705 if (siteWindow) {
6706 // This method call may cause mDocShell to become nullptr.
6707 siteWindow->Blur();
6709 // if the root is focused, clear the focus
6710 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6711 if (fm && mDoc) {
6712 nsCOMPtr<nsIDOMElement> element;
6713 fm->GetFocusedElementForWindow(this, false, nullptr, getter_AddRefs(element));
6714 nsCOMPtr<nsIContent> content = do_QueryInterface(element);
6715 if (content == mDoc->GetRootElement())
6716 fm->ClearFocus(this);
6721 NS_IMETHODIMP
6722 nsGlobalWindow::Blur()
6724 ErrorResult rv;
6725 Blur(rv);
6727 return rv.ErrorCode();
6730 void
6731 nsGlobalWindow::Back(ErrorResult& aError)
6733 FORWARD_TO_OUTER_OR_THROW(Back, (aError), aError, );
6735 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6736 if (!webNav) {
6737 aError.Throw(NS_ERROR_FAILURE);
6738 return;
6741 aError = webNav->GoBack();
6744 NS_IMETHODIMP
6745 nsGlobalWindow::Back()
6747 ErrorResult rv;
6748 Back(rv);
6750 return rv.ErrorCode();
6753 void
6754 nsGlobalWindow::Forward(ErrorResult& aError)
6756 FORWARD_TO_OUTER_OR_THROW(Forward, (aError), aError, );
6758 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6759 if (!webNav) {
6760 aError.Throw(NS_ERROR_FAILURE);
6761 return;
6764 aError = webNav->GoForward();
6767 NS_IMETHODIMP
6768 nsGlobalWindow::Forward()
6770 ErrorResult rv;
6771 Forward(rv);
6773 return rv.ErrorCode();
6776 void
6777 nsGlobalWindow::Home(ErrorResult& aError)
6779 FORWARD_TO_OUTER_OR_THROW(Home, (aError), aError, );
6781 if (!mDocShell) {
6782 return;
6785 nsAdoptingString homeURL =
6786 Preferences::GetLocalizedString(PREF_BROWSER_STARTUP_HOMEPAGE);
6788 if (homeURL.IsEmpty()) {
6789 // if all else fails, use this
6790 #ifdef DEBUG_seth
6791 printf("all else failed. using %s as the home page\n", DEFAULT_HOME_PAGE);
6792 #endif
6793 CopyASCIItoUTF16(DEFAULT_HOME_PAGE, homeURL);
6796 #ifdef MOZ_PHOENIX
6798 // Firefox lets the user specify multiple home pages to open in
6799 // individual tabs by separating them with '|'. Since we don't
6800 // have the machinery in place to easily open new tabs from here,
6801 // simply truncate the homeURL at the first '|' character to
6802 // prevent any possibilities of leaking the users list of home
6803 // pages to the first home page.
6805 // Once bug https://bugzilla.mozilla.org/show_bug.cgi?id=221445 is
6806 // fixed we can revisit this.
6807 int32_t firstPipe = homeURL.FindChar('|');
6809 if (firstPipe > 0) {
6810 homeURL.Truncate(firstPipe);
6813 #endif
6815 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6816 if (!webNav) {
6817 aError.Throw(NS_ERROR_FAILURE);
6818 return;
6821 aError = webNav->LoadURI(homeURL.get(),
6822 nsIWebNavigation::LOAD_FLAGS_NONE,
6823 nullptr,
6824 nullptr,
6825 nullptr);
6828 NS_IMETHODIMP
6829 nsGlobalWindow::Home()
6831 ErrorResult rv;
6832 Home(rv);
6834 return rv.ErrorCode();
6837 void
6838 nsGlobalWindow::Stop(ErrorResult& aError)
6840 FORWARD_TO_OUTER_OR_THROW(Stop, (aError), aError, );
6842 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6843 if (webNav) {
6844 aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
6848 NS_IMETHODIMP
6849 nsGlobalWindow::Stop()
6851 ErrorResult rv;
6852 Stop(rv);
6854 return rv.ErrorCode();
6857 void
6858 nsGlobalWindow::Print(ErrorResult& aError)
6860 #ifdef NS_PRINTING
6861 FORWARD_TO_OUTER_OR_THROW(Print, (aError), aError, );
6863 if (Preferences::GetBool("dom.disable_window_print", false)) {
6864 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6865 return;
6868 if (!AreDialogsEnabled()) {
6869 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6870 return;
6873 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
6874 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6875 return;
6878 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
6879 if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
6880 getter_AddRefs(webBrowserPrint)))) {
6881 nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
6882 GetCurrentInnerWindowInternal()->mDoc :
6883 nullptr);
6885 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
6886 do_GetService("@mozilla.org/gfx/printsettings-service;1");
6888 nsCOMPtr<nsIPrintSettings> printSettings;
6889 if (printSettingsService) {
6890 bool printSettingsAreGlobal =
6891 Preferences::GetBool("print.use_global_printsettings", false);
6893 if (printSettingsAreGlobal) {
6894 printSettingsService->GetGlobalPrintSettings(getter_AddRefs(printSettings));
6896 nsXPIDLString printerName;
6897 printSettings->GetPrinterName(getter_Copies(printerName));
6898 if (printerName.IsEmpty()) {
6899 printSettingsService->GetDefaultPrinterName(getter_Copies(printerName));
6900 printSettings->SetPrinterName(printerName);
6902 printSettingsService->InitPrintSettingsFromPrinter(printerName, printSettings);
6903 printSettingsService->InitPrintSettingsFromPrefs(printSettings,
6904 true,
6905 nsIPrintSettings::kInitSaveAll);
6906 } else {
6907 printSettingsService->GetNewPrintSettings(getter_AddRefs(printSettings));
6910 EnterModalState();
6911 webBrowserPrint->Print(printSettings, nullptr);
6912 LeaveModalState();
6914 bool savePrintSettings =
6915 Preferences::GetBool("print.save_print_settings", false);
6916 if (printSettingsAreGlobal && savePrintSettings) {
6917 printSettingsService->
6918 SavePrintSettingsToPrefs(printSettings,
6919 true,
6920 nsIPrintSettings::kInitSaveAll);
6921 printSettingsService->
6922 SavePrintSettingsToPrefs(printSettings,
6923 false,
6924 nsIPrintSettings::kInitSavePrinterName);
6926 } else {
6927 webBrowserPrint->GetGlobalPrintSettings(getter_AddRefs(printSettings));
6928 webBrowserPrint->Print(printSettings, nullptr);
6931 #endif //NS_PRINTING
6934 NS_IMETHODIMP
6935 nsGlobalWindow::Print()
6937 ErrorResult rv;
6938 Print(rv);
6940 return rv.ErrorCode();
6943 void
6944 nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos, ErrorResult& aError)
6946 FORWARD_TO_OUTER_OR_THROW(MoveTo, (aXPos, aYPos, aError), aError, );
6949 * If caller is not chrome and the user has not explicitly exempted the site,
6950 * prevent window.moveTo() by exiting early
6953 if (!CanMoveResizeWindows() || IsFrame()) {
6954 return;
6957 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6958 if (!treeOwnerAsWin) {
6959 aError.Throw(NS_ERROR_FAILURE);
6960 return;
6963 // Mild abuse of a "size" object so we don't need more helper functions.
6964 nsIntSize cssPos(aXPos, aYPos);
6965 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
6967 nsIntSize devPos = CSSToDevIntPixels(cssPos);
6969 aError = treeOwnerAsWin->SetPosition(devPos.width, devPos.height);
6972 NS_IMETHODIMP
6973 nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos)
6975 ErrorResult rv;
6976 MoveTo(aXPos, aYPos, rv);
6978 return rv.ErrorCode();
6981 void
6982 nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif, ErrorResult& aError)
6984 FORWARD_TO_OUTER_OR_THROW(MoveBy, (aXDif, aYDif, aError), aError, );
6987 * If caller is not chrome and the user has not explicitly exempted the site,
6988 * prevent window.moveBy() by exiting early
6991 if (!CanMoveResizeWindows() || IsFrame()) {
6992 return;
6995 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6996 if (!treeOwnerAsWin) {
6997 aError.Throw(NS_ERROR_FAILURE);
6998 return;
7001 // To do this correctly we have to convert what we get from GetPosition
7002 // into CSS pixels, add the arguments, do the security check, and
7003 // then convert back to device pixels for the call to SetPosition.
7005 int32_t x, y;
7006 aError = treeOwnerAsWin->GetPosition(&x, &y);
7007 if (aError.Failed()) {
7008 return;
7011 // mild abuse of a "size" object so we don't need more helper functions
7012 nsIntSize cssPos(DevToCSSIntPixels(nsIntSize(x, y)));
7014 cssPos.width += aXDif;
7015 cssPos.height += aYDif;
7017 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
7019 nsIntSize newDevPos(CSSToDevIntPixels(cssPos));
7021 aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height);
7024 NS_IMETHODIMP
7025 nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif)
7027 ErrorResult rv;
7028 MoveBy(aXDif, aYDif, rv);
7030 return rv.ErrorCode();
7033 void
7034 nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight, ErrorResult& aError)
7036 FORWARD_TO_OUTER_OR_THROW(ResizeTo, (aWidth, aHeight, aError), aError, );
7039 * If caller is a browser-element then dispatch a resize event to
7040 * the embedder.
7042 if (mDocShell && mDocShell->GetIsBrowserOrApp()) {
7043 CSSIntSize size(aWidth, aHeight);
7044 if (!DispatchResizeEvent(size)) {
7045 // The embedder chose to prevent the default action for this
7046 // event, so let's not resize this window after all...
7047 return;
7052 * If caller is not chrome and the user has not explicitly exempted the site,
7053 * prevent window.resizeTo() by exiting early
7056 if (!CanMoveResizeWindows() || IsFrame()) {
7057 return;
7060 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
7061 if (!treeOwnerAsWin) {
7062 aError.Throw(NS_ERROR_FAILURE);
7063 return;
7066 nsIntSize cssSize(aWidth, aHeight);
7067 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7069 nsIntSize devSz(CSSToDevIntPixels(cssSize));
7071 aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true);
7074 NS_IMETHODIMP
7075 nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight)
7077 ErrorResult rv;
7078 ResizeTo(aWidth, aHeight, rv);
7080 return rv.ErrorCode();
7083 void
7084 nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
7085 ErrorResult& aError)
7087 FORWARD_TO_OUTER_OR_THROW(ResizeBy, (aWidthDif, aHeightDif, aError), aError, );
7090 * If caller is a browser-element then dispatch a resize event to
7091 * parent.
7093 if (mDocShell && mDocShell->GetIsBrowserOrApp()) {
7094 CSSIntSize size;
7095 if (NS_FAILED(GetInnerSize(size))) {
7096 return;
7099 size.width += aWidthDif;
7100 size.height += aHeightDif;
7102 if (!DispatchResizeEvent(size)) {
7103 // The embedder chose to prevent the default action for this
7104 // event, so let's not resize this window after all...
7105 return;
7110 * If caller is not chrome and the user has not explicitly exempted the site,
7111 * prevent window.resizeBy() by exiting early
7114 if (!CanMoveResizeWindows() || IsFrame()) {
7115 return;
7118 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
7119 if (!treeOwnerAsWin) {
7120 aError.Throw(NS_ERROR_FAILURE);
7121 return;
7124 int32_t width, height;
7125 aError = treeOwnerAsWin->GetSize(&width, &height);
7126 if (aError.Failed()) {
7127 return;
7130 // To do this correctly we have to convert what we got from GetSize
7131 // into CSS pixels, add the arguments, do the security check, and
7132 // then convert back to device pixels for the call to SetSize.
7134 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
7136 cssSize.width += aWidthDif;
7137 cssSize.height += aHeightDif;
7139 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7141 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
7143 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
7146 NS_IMETHODIMP
7147 nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif)
7149 ErrorResult rv;
7150 ResizeBy(aWidthDif, aHeightDif, rv);
7152 return rv.ErrorCode();
7155 void
7156 nsGlobalWindow::SizeToContent(ErrorResult& aError)
7158 FORWARD_TO_OUTER_OR_THROW(SizeToContent, (aError), aError, );
7160 if (!mDocShell) {
7161 return;
7165 * If caller is not chrome and the user has not explicitly exempted the site,
7166 * prevent window.sizeToContent() by exiting early
7169 if (!CanMoveResizeWindows() || IsFrame()) {
7170 return;
7173 // The content viewer does a check to make sure that it's a content
7174 // viewer for a toplevel docshell.
7175 nsCOMPtr<nsIContentViewer> cv;
7176 mDocShell->GetContentViewer(getter_AddRefs(cv));
7177 if (!cv) {
7178 aError.Throw(NS_ERROR_FAILURE);
7179 return;
7182 int32_t width, height;
7183 aError = cv->GetContentSize(&width, &height);
7184 if (aError.Failed()) {
7185 return;
7188 // Make sure the new size is following the CheckSecurityWidthAndHeight
7189 // rules.
7190 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
7191 if (!treeOwner) {
7192 aError.Throw(NS_ERROR_FAILURE);
7193 return;
7196 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
7197 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7199 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
7201 aError = treeOwner->SizeShellTo(mDocShell, newDevSize.width,
7202 newDevSize.height);
7205 NS_IMETHODIMP
7206 nsGlobalWindow::SizeToContent()
7208 ErrorResult rv;
7209 SizeToContent(rv);
7211 return rv.ErrorCode();
7214 NS_IMETHODIMP
7215 nsGlobalWindow::GetWindowRoot(nsIDOMEventTarget **aWindowRoot)
7217 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
7218 return CallQueryInterface(root, aWindowRoot);
7221 already_AddRefed<nsPIWindowRoot>
7222 nsGlobalWindow::GetTopWindowRoot()
7224 nsPIDOMWindow* piWin = GetPrivateRoot();
7225 if (!piWin) {
7226 return nullptr;
7229 nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler());
7230 return window.forget();
7233 void
7234 nsGlobalWindow::Scroll(double aXScroll, double aYScroll,
7235 const ScrollOptions& aOptions)
7237 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
7238 CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
7239 mozilla::ToZeroIfNonfinite(aYScroll));
7240 ScrollTo(scrollPos, aOptions);
7243 void
7244 nsGlobalWindow::ScrollTo(double aXScroll, double aYScroll,
7245 const ScrollOptions& aOptions)
7247 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
7248 CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
7249 mozilla::ToZeroIfNonfinite(aYScroll));
7250 ScrollTo(scrollPos, aOptions);
7253 NS_IMETHODIMP
7254 nsGlobalWindow::Scroll(int32_t aXScroll, int32_t aYScroll)
7256 ScrollTo(CSSIntPoint(aXScroll, aYScroll), ScrollOptions());
7257 return NS_OK;
7260 NS_IMETHODIMP
7261 nsGlobalWindow::ScrollTo(int32_t aXScroll, int32_t aYScroll)
7263 ScrollTo(CSSIntPoint(aXScroll, aYScroll), ScrollOptions());
7264 return NS_OK;
7267 void
7268 nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll,
7269 const ScrollOptions& aOptions)
7271 FlushPendingNotifications(Flush_Layout);
7272 nsIScrollableFrame *sf = GetScrollFrame();
7274 if (sf) {
7275 // Here we calculate what the max pixel value is that we can
7276 // scroll to, we do this by dividing maxint with the pixel to
7277 // twips conversion factor, and subtracting 4, the 4 comes from
7278 // experimenting with this value, anything less makes the view
7279 // code not scroll correctly, I have no idea why. -- jst
7280 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
7282 CSSIntPoint scroll(aScroll);
7283 if (scroll.x > maxpx) {
7284 scroll.x = maxpx;
7287 if (scroll.y > maxpx) {
7288 scroll.y = maxpx;
7291 sf->ScrollToCSSPixels(scroll,
7292 aOptions.mBehavior == ScrollBehavior::Smooth
7293 ? nsIScrollableFrame::SMOOTH_MSD
7294 : nsIScrollableFrame::INSTANT);
7298 NS_IMETHODIMP
7299 nsGlobalWindow::ScrollBy(int32_t aXScrollDif, int32_t aYScrollDif)
7301 ScrollBy(aXScrollDif, aYScrollDif, ScrollOptions());
7303 return NS_OK;
7306 void
7307 nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif,
7308 const ScrollOptions& aOptions)
7310 FlushPendingNotifications(Flush_Layout);
7311 nsIScrollableFrame *sf = GetScrollFrame();
7313 if (sf) {
7314 // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
7315 CSSIntPoint scrollDif(mozilla::ToZeroIfNonfinite(aXScrollDif),
7316 mozilla::ToZeroIfNonfinite(aYScrollDif));
7317 // It seems like it would make more sense for ScrollBy to use
7318 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
7319 // Perhaps Web content does too.
7320 ScrollTo(sf->GetScrollPositionCSSPixels() + scrollDif, aOptions);
7324 NS_IMETHODIMP
7325 nsGlobalWindow::ScrollByLines(int32_t numLines)
7327 ScrollByLines(numLines, ScrollOptions());
7329 return NS_OK;
7332 void
7333 nsGlobalWindow::ScrollByLines(int32_t numLines,
7334 const ScrollOptions& aOptions)
7336 FlushPendingNotifications(Flush_Layout);
7337 nsIScrollableFrame *sf = GetScrollFrame();
7338 if (sf) {
7339 // It seems like it would make more sense for ScrollByLines to use
7340 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
7341 // Perhaps Web content does too.
7342 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
7343 aOptions.mBehavior == ScrollBehavior::Smooth
7344 ? nsIScrollableFrame::SMOOTH_MSD
7345 : nsIScrollableFrame::INSTANT);
7349 NS_IMETHODIMP
7350 nsGlobalWindow::ScrollByPages(int32_t numPages)
7352 ScrollByPages(numPages, ScrollOptions());
7354 return NS_OK;
7357 void
7358 nsGlobalWindow::ScrollByPages(int32_t numPages,
7359 const ScrollOptions& aOptions)
7361 FlushPendingNotifications(Flush_Layout);
7362 nsIScrollableFrame *sf = GetScrollFrame();
7363 if (sf) {
7364 // It seems like it would make more sense for ScrollByPages to use
7365 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
7366 // Perhaps Web content does too.
7367 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
7368 aOptions.mBehavior == ScrollBehavior::Smooth
7369 ? nsIScrollableFrame::SMOOTH_MSD
7370 : nsIScrollableFrame::INSTANT);
7374 void
7375 nsGlobalWindow::MozRequestOverfill(OverfillCallback& aCallback,
7376 mozilla::ErrorResult& aError)
7378 nsIWidget* widget = nsContentUtils::WidgetForDocument(mDoc);
7379 if (widget) {
7380 mozilla::layers::LayerManager* manager = widget->GetLayerManager();
7381 if (manager) {
7382 manager->RequestOverfill(&aCallback);
7383 return;
7387 aError.Throw(NS_ERROR_NOT_AVAILABLE);
7390 void
7391 nsGlobalWindow::ClearTimeout(int32_t aHandle, ErrorResult& aError)
7393 if (aHandle > 0) {
7394 ClearTimeoutOrInterval(aHandle, aError);
7398 NS_IMETHODIMP
7399 nsGlobalWindow::ClearTimeout(int32_t aHandle)
7401 ErrorResult rv;
7402 ClearTimeout(aHandle, rv);
7404 return rv.ErrorCode();
7407 void
7408 nsGlobalWindow::ClearInterval(int32_t aHandle, ErrorResult& aError)
7410 if (aHandle > 0) {
7411 ClearTimeoutOrInterval(aHandle, aError);
7415 NS_IMETHODIMP
7416 nsGlobalWindow::ClearInterval(int32_t aHandle)
7418 ErrorResult rv;
7419 ClearInterval(aHandle, rv);
7421 return rv.ErrorCode();
7424 NS_IMETHODIMP
7425 nsGlobalWindow::SetTimeout(int32_t *_retval)
7427 return SetTimeoutOrInterval(false, _retval);
7430 NS_IMETHODIMP
7431 nsGlobalWindow::SetInterval(int32_t *_retval)
7433 return SetTimeoutOrInterval(true, _retval);
7436 NS_IMETHODIMP
7437 nsGlobalWindow::SetResizable(bool aResizable)
7439 // nop
7441 return NS_OK;
7444 NS_IMETHODIMP
7445 nsGlobalWindow::CaptureEvents()
7447 if (mDoc) {
7448 mDoc->WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
7451 return NS_OK;
7454 NS_IMETHODIMP
7455 nsGlobalWindow::ReleaseEvents()
7457 if (mDoc) {
7458 mDoc->WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
7461 return NS_OK;
7464 static
7465 bool IsPopupBlocked(nsIDocument* aDoc)
7467 nsCOMPtr<nsIPopupWindowManager> pm =
7468 do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
7470 if (!pm) {
7471 return false;
7474 if (!aDoc) {
7475 return true;
7478 uint32_t permission = nsIPopupWindowManager::ALLOW_POPUP;
7479 pm->TestPermission(aDoc->NodePrincipal(), &permission);
7480 return permission == nsIPopupWindowManager::DENY_POPUP;
7483 void
7484 nsGlobalWindow::FirePopupBlockedEvent(nsIDocument* aDoc,
7485 nsIURI* aPopupURI,
7486 const nsAString& aPopupWindowName,
7487 const nsAString& aPopupWindowFeatures)
7489 MOZ_ASSERT(aDoc);
7491 // Fire a "DOMPopupBlocked" event so that the UI can hear about
7492 // blocked popups.
7493 PopupBlockedEventInit init;
7494 init.mBubbles = true;
7495 init.mCancelable = true;
7496 init.mRequestingWindow = this;
7497 init.mPopupWindowURI = aPopupURI;
7498 init.mPopupWindowName = aPopupWindowName;
7499 init.mPopupWindowFeatures = aPopupWindowFeatures;
7501 nsRefPtr<PopupBlockedEvent> event =
7502 PopupBlockedEvent::Constructor(aDoc,
7503 NS_LITERAL_STRING("DOMPopupBlocked"),
7504 init);
7506 event->SetTrusted(true);
7508 bool defaultActionEnabled;
7509 aDoc->DispatchEvent(event, &defaultActionEnabled);
7512 static void FirePopupWindowEvent(nsIDocument* aDoc)
7514 // Fire a "PopupWindow" event
7515 nsContentUtils::DispatchTrustedEvent(aDoc, aDoc,
7516 NS_LITERAL_STRING("PopupWindow"),
7517 true, true);
7520 // static
7521 bool
7522 nsGlobalWindow::CanSetProperty(const char *aPrefName)
7524 // Chrome can set any property.
7525 if (nsContentUtils::IsCallerChrome()) {
7526 return true;
7529 // If the pref is set to true, we can not set the property
7530 // and vice versa.
7531 return !Preferences::GetBool(aPrefName, true);
7534 bool
7535 nsGlobalWindow::PopupWhitelisted()
7537 if (!IsPopupBlocked(mDoc))
7538 return true;
7540 nsCOMPtr<nsIDOMWindow> parent;
7542 if (NS_FAILED(GetParent(getter_AddRefs(parent))) ||
7543 parent == static_cast<nsIDOMWindow*>(this))
7545 return false;
7548 return static_cast<nsGlobalWindow*>
7549 (static_cast<nsIDOMWindow*>
7550 (parent.get()))->PopupWhitelisted();
7554 * Examine the current document state to see if we're in a way that is
7555 * typically abused by web designers. The window.open code uses this
7556 * routine to determine whether to allow the new window.
7557 * Returns a value from the PopupControlState enum.
7559 PopupControlState
7560 nsGlobalWindow::RevisePopupAbuseLevel(PopupControlState aControl)
7562 MOZ_ASSERT(IsOuterWindow());
7564 NS_ASSERTION(mDocShell, "Must have docshell");
7566 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
7567 return openAllowed;
7570 PopupControlState abuse = aControl;
7571 switch (abuse) {
7572 case openControlled:
7573 case openAbused:
7574 case openOverridden:
7575 if (PopupWhitelisted())
7576 abuse = PopupControlState(abuse - 1);
7577 case openAllowed: break;
7578 default:
7579 NS_WARNING("Strange PopupControlState!");
7582 // limit the number of simultaneously open popups
7583 if (abuse == openAbused || abuse == openControlled) {
7584 int32_t popupMax = Preferences::GetInt("dom.popup_maximum", -1);
7585 if (popupMax >= 0 && gOpenPopupSpamCount >= popupMax)
7586 abuse = openOverridden;
7589 return abuse;
7592 /* If a window open is blocked, fire the appropriate DOM events.
7593 aBlocked signifies we just blocked a popup.
7594 aWindow signifies we just opened what is probably a popup.
7596 void
7597 nsGlobalWindow::FireAbuseEvents(bool aBlocked, bool aWindow,
7598 const nsAString &aPopupURL,
7599 const nsAString &aPopupWindowName,
7600 const nsAString &aPopupWindowFeatures)
7602 // fetch the URI of the window requesting the opened window
7604 nsCOMPtr<nsIDOMWindow> topWindow;
7605 GetTop(getter_AddRefs(topWindow));
7606 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(topWindow);
7607 if (!window) {
7608 return;
7611 nsCOMPtr<nsIDocument> topDoc = window->GetDoc();
7612 nsCOMPtr<nsIURI> popupURI;
7614 // build the URI of the would-have-been popup window
7615 // (see nsWindowWatcher::URIfromURL)
7617 // first, fetch the opener's base URI
7619 nsIURI *baseURL = nullptr;
7621 nsCOMPtr<nsIDocument> doc = GetEntryDocument();
7622 if (doc)
7623 baseURL = doc->GetDocBaseURI();
7625 // use the base URI to build what would have been the popup's URI
7626 nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
7627 if (ios)
7628 ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), 0, baseURL,
7629 getter_AddRefs(popupURI));
7631 // fire an event chock full of informative URIs
7632 if (aBlocked) {
7633 FirePopupBlockedEvent(topDoc, popupURI, aPopupWindowName,
7634 aPopupWindowFeatures);
7636 if (aWindow)
7637 FirePopupWindowEvent(topDoc);
7640 already_AddRefed<nsIDOMWindow>
7641 nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
7642 const nsAString& aOptions, ErrorResult& aError)
7644 FORWARD_TO_OUTER_OR_THROW(Open, (aUrl, aName, aOptions, aError), aError,
7645 nullptr);
7646 nsCOMPtr<nsIDOMWindow> window;
7647 aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window));
7648 return window.forget();
7651 NS_IMETHODIMP
7652 nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
7653 const nsAString& aOptions, nsIDOMWindow **_retval)
7655 FORWARD_TO_OUTER(Open, (aUrl, aName, aOptions, _retval),
7656 NS_ERROR_NOT_INITIALIZED);
7657 return OpenInternal(aUrl, aName, aOptions,
7658 false, // aDialog
7659 false, // aContentModal
7660 true, // aCalledNoScript
7661 false, // aDoJSFixups
7662 true, // aNavigate
7663 nullptr, nullptr, // No args
7664 GetPrincipal(), // aCalleePrincipal
7665 nullptr, // aJSCallerContext
7666 _retval);
7669 NS_IMETHODIMP
7670 nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName,
7671 const nsAString& aOptions, nsIDOMWindow **_retval)
7673 FORWARD_TO_OUTER(OpenJS, (aUrl, aName, aOptions, _retval),
7674 NS_ERROR_NOT_INITIALIZED);
7675 return OpenInternal(aUrl, aName, aOptions,
7676 false, // aDialog
7677 false, // aContentModal
7678 false, // aCalledNoScript
7679 true, // aDoJSFixups
7680 true, // aNavigate
7681 nullptr, nullptr, // No args
7682 GetPrincipal(), // aCalleePrincipal
7683 nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
7684 _retval);
7687 // like Open, but attaches to the new window any extra parameters past
7688 // [features] as a JS property named "arguments"
7689 NS_IMETHODIMP
7690 nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
7691 const nsAString& aOptions,
7692 nsISupports* aExtraArgument, nsIDOMWindow** _retval)
7694 FORWARD_TO_OUTER(OpenDialog, (aUrl, aName, aOptions, aExtraArgument, _retval),
7695 NS_ERROR_NOT_INITIALIZED);
7696 return OpenInternal(aUrl, aName, aOptions,
7697 true, // aDialog
7698 false, // aContentModal
7699 true, // aCalledNoScript
7700 false, // aDoJSFixups
7701 true, // aNavigate
7702 nullptr, aExtraArgument, // Arguments
7703 GetPrincipal(), // aCalleePrincipal
7704 nullptr, // aJSCallerContext
7705 _retval);
7708 // Like Open, but passes aNavigate=false.
7709 /* virtual */ nsresult
7710 nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
7711 const nsAString& aName,
7712 const nsAString& aOptions,
7713 nsIDOMWindow **_retval)
7715 MOZ_ASSERT(IsOuterWindow());
7716 return OpenInternal(aUrl, aName, aOptions,
7717 false, // aDialog
7718 false, // aContentModal
7719 true, // aCalledNoScript
7720 false, // aDoJSFixups
7721 false, // aNavigate
7722 nullptr, nullptr, // No args
7723 GetPrincipal(), // aCalleePrincipal
7724 nullptr, // aJSCallerContext
7725 _retval);
7729 already_AddRefed<nsIDOMWindow>
7730 nsGlobalWindow::OpenDialog(JSContext* aCx, const nsAString& aUrl,
7731 const nsAString& aName, const nsAString& aOptions,
7732 const Sequence<JS::Value>& aExtraArgument,
7733 ErrorResult& aError)
7735 FORWARD_TO_OUTER_OR_THROW(OpenDialog,
7736 (aCx, aUrl, aName, aOptions, aExtraArgument, aError),
7737 aError, nullptr);
7739 nsCOMPtr<nsIJSArgArray> argvArray;
7740 aError = NS_CreateJSArgv(aCx, aExtraArgument.Length(),
7741 const_cast<JS::Value*>(aExtraArgument.Elements()),
7742 getter_AddRefs(argvArray));
7743 if (aError.Failed()) {
7744 return nullptr;
7747 nsCOMPtr<nsIDOMWindow> dialog;
7748 aError = OpenInternal(aUrl, aName, aOptions,
7749 true, // aDialog
7750 false, // aContentModal
7751 false, // aCalledNoScript
7752 false, // aDoJSFixups
7753 true, // aNavigate
7754 argvArray, nullptr, // Arguments
7755 GetPrincipal(), // aCalleePrincipal
7756 aCx, // aJSCallerContext
7757 getter_AddRefs(dialog));
7758 return dialog.forget();
7761 NS_IMETHODIMP
7762 nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
7763 const nsAString& aOptions, nsIDOMWindow** _retval)
7765 FORWARD_TO_OUTER(OpenDialog, (aUrl, aName, aOptions, _retval),
7766 NS_ERROR_NOT_INITIALIZED);
7768 if (!nsContentUtils::IsCallerChrome()) {
7769 return NS_ERROR_DOM_SECURITY_ERR;
7772 nsAXPCNativeCallContext *ncc = nullptr;
7773 nsresult rv = nsContentUtils::XPConnect()->
7774 GetCurrentNativeCallContext(&ncc);
7775 NS_ENSURE_SUCCESS(rv, rv);
7777 if (!ncc)
7778 return NS_ERROR_NOT_AVAILABLE;
7780 JSContext *cx = nullptr;
7782 rv = ncc->GetJSContext(&cx);
7783 NS_ENSURE_SUCCESS(rv, rv);
7785 uint32_t argc;
7786 JS::Value *argv = nullptr;
7788 // XXX - need to get this as nsISupports?
7789 ncc->GetArgc(&argc);
7790 ncc->GetArgvPtr(&argv);
7792 // Strip the url, name and options from the args seen by scripts.
7793 uint32_t argOffset = argc < 3 ? argc : 3;
7794 nsCOMPtr<nsIJSArgArray> argvArray;
7795 rv = NS_CreateJSArgv(cx, argc - argOffset, argv + argOffset,
7796 getter_AddRefs(argvArray));
7797 NS_ENSURE_SUCCESS(rv, rv);
7799 return OpenInternal(aUrl, aName, aOptions,
7800 true, // aDialog
7801 false, // aContentModal
7802 false, // aCalledNoScript
7803 false, // aDoJSFixups
7804 true, // aNavigate
7805 argvArray, nullptr, // Arguments
7806 GetPrincipal(), // aCalleePrincipal
7807 cx, // aJSCallerContext
7808 _retval);
7811 already_AddRefed<nsIDOMWindow>
7812 nsGlobalWindow::GetFrames(ErrorResult& aError)
7814 FORWARD_TO_OUTER_OR_THROW(GetFrames, (aError), aError, nullptr);
7816 nsRefPtr<nsGlobalWindow> frames(this);
7817 FlushPendingNotifications(Flush_ContentAndNotify);
7818 return frames.forget();
7821 NS_IMETHODIMP
7822 nsGlobalWindow::GetFrames(nsIDOMWindow** aFrames)
7824 ErrorResult rv;
7825 nsCOMPtr<nsIDOMWindow> frames = GetFrames(rv);
7826 frames.forget(aFrames);
7828 return rv.ErrorCode();
7831 nsGlobalWindow*
7832 nsGlobalWindow::CallerInnerWindow()
7834 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7835 NS_ENSURE_TRUE(cx, nullptr);
7836 nsIGlobalObject* global = GetIncumbentGlobal();
7837 NS_ENSURE_TRUE(global, nullptr);
7838 JS::Rooted<JSObject*> scope(cx, global->GetGlobalJSObject());
7839 NS_ENSURE_TRUE(scope, nullptr);
7841 // When Jetpack runs content scripts inside a sandbox, it uses
7842 // sandboxPrototype to make them appear as though they're running in the
7843 // scope of the page. So when a content script invokes postMessage, it expects
7844 // the |source| of the received message to be the window set as the
7845 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
7846 // now we need to do some special handling to support it.
7847 if (xpc::IsSandbox(scope)) {
7848 JSAutoCompartment ac(cx, scope);
7849 JS::Rooted<JSObject*> scopeProto(cx);
7850 bool ok = JS_GetPrototype(cx, scope, &scopeProto);
7851 NS_ENSURE_TRUE(ok, nullptr);
7852 if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) &&
7853 (scopeProto = js::CheckedUnwrap(scopeProto, /* stopAtOuter = */ false)))
7855 global = xpc::GetNativeForGlobal(scopeProto);
7856 NS_ENSURE_TRUE(global, nullptr);
7860 // The calling window must be holding a reference, so we can return a weak
7861 // pointer.
7862 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global);
7863 return static_cast<nsGlobalWindow*>(win.get());
7867 * Class used to represent events generated by calls to Window.postMessage,
7868 * which asynchronously creates and dispatches events.
7870 class PostMessageEvent : public nsRunnable
7872 public:
7873 NS_DECL_NSIRUNNABLE
7875 PostMessageEvent(nsGlobalWindow* aSource,
7876 const nsAString& aCallerOrigin,
7877 nsGlobalWindow* aTargetWindow,
7878 nsIPrincipal* aProvidedPrincipal,
7879 bool aTrustedCaller)
7880 : mSource(aSource),
7881 mCallerOrigin(aCallerOrigin),
7882 mTargetWindow(aTargetWindow),
7883 mProvidedPrincipal(aProvidedPrincipal),
7884 mTrustedCaller(aTrustedCaller)
7886 MOZ_COUNT_CTOR(PostMessageEvent);
7889 protected:
7890 ~PostMessageEvent()
7892 MOZ_COUNT_DTOR(PostMessageEvent);
7895 public:
7896 JSAutoStructuredCloneBuffer& Buffer()
7898 return mBuffer;
7901 bool StoreISupports(nsISupports* aSupports)
7903 mSupportsArray.AppendElement(aSupports);
7904 return true;
7907 private:
7908 JSAutoStructuredCloneBuffer mBuffer;
7909 nsRefPtr<nsGlobalWindow> mSource;
7910 nsString mCallerOrigin;
7911 nsRefPtr<nsGlobalWindow> mTargetWindow;
7912 nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
7913 bool mTrustedCaller;
7914 nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
7917 namespace {
7919 struct StructuredCloneInfo {
7920 PostMessageEvent* event;
7921 bool subsumes;
7922 nsPIDOMWindow* window;
7923 nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
7926 static JSObject*
7927 PostMessageReadStructuredClone(JSContext* cx,
7928 JSStructuredCloneReader* reader,
7929 uint32_t tag,
7930 uint32_t data,
7931 void* closure)
7933 if (tag == SCTAG_DOM_BLOB) {
7934 NS_ASSERTION(!data, "Data should be empty");
7936 // What we get back from the reader is a DOMFileImpl.
7937 // From that we create a new DOMFile.
7938 nsISupports* supports;
7939 if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
7940 nsCOMPtr<nsIDOMBlob> file = new DOMFile(static_cast<DOMFileImpl*>(supports));
7941 JS::Rooted<JS::Value> val(cx);
7942 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, file, &val))) {
7943 return val.toObjectOrNull();
7948 if (tag == SCTAG_DOM_FILELIST) {
7949 NS_ASSERTION(!data, "Data should be empty");
7951 nsISupports* supports;
7952 if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
7953 JS::Rooted<JS::Value> val(cx);
7954 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
7955 return val.toObjectOrNull();
7960 const JSStructuredCloneCallbacks* runtimeCallbacks =
7961 js::GetContextStructuredCloneCallbacks(cx);
7963 if (runtimeCallbacks) {
7964 return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
7967 return nullptr;
7970 static bool
7971 PostMessageWriteStructuredClone(JSContext* cx,
7972 JSStructuredCloneWriter* writer,
7973 JS::Handle<JSObject*> obj,
7974 void *closure)
7976 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
7977 NS_ASSERTION(scInfo, "Must have scInfo!");
7979 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
7980 nsContentUtils::XPConnect()->
7981 GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
7982 if (wrappedNative) {
7983 uint32_t scTag = 0;
7984 nsISupports* supports = wrappedNative->Native();
7986 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
7987 if (blob && scInfo->subsumes) {
7988 scTag = SCTAG_DOM_BLOB;
7989 DOMFile* file = static_cast<DOMFile*>(blob.get());
7990 supports = file->Impl();
7993 nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
7994 if (list && scInfo->subsumes)
7995 scTag = SCTAG_DOM_FILELIST;
7997 if (scTag)
7998 return JS_WriteUint32Pair(writer, scTag, 0) &&
7999 JS_WriteBytes(writer, &supports, sizeof(supports)) &&
8000 scInfo->event->StoreISupports(supports);
8003 const JSStructuredCloneCallbacks* runtimeCallbacks =
8004 js::GetContextStructuredCloneCallbacks(cx);
8006 if (runtimeCallbacks) {
8007 return runtimeCallbacks->write(cx, writer, obj, nullptr);
8010 return false;
8013 static bool
8014 PostMessageReadTransferStructuredClone(JSContext* aCx,
8015 JSStructuredCloneReader* reader,
8016 uint32_t tag, void* aData,
8017 uint64_t aExtraData,
8018 void* aClosure,
8019 JS::MutableHandle<JSObject*> returnObject)
8021 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
8022 NS_ASSERTION(scInfo, "Must have scInfo!");
8024 if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
8025 MessagePort* port = static_cast<MessagePort*>(aData);
8026 port->BindToOwner(scInfo->window);
8027 scInfo->ports.Put(port, nullptr);
8029 JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx));
8030 if (JS_WrapObject(aCx, &obj)) {
8031 MOZ_ASSERT(port->GetOwner() == scInfo->window);
8032 returnObject.set(obj);
8035 return true;
8038 return false;
8041 static bool
8042 PostMessageTransferStructuredClone(JSContext* aCx,
8043 JS::Handle<JSObject*> aObj,
8044 void* aClosure,
8045 uint32_t* aTag,
8046 JS::TransferableOwnership* aOwnership,
8047 void** aContent,
8048 uint64_t* aExtraData)
8050 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
8051 NS_ASSERTION(scInfo, "Must have scInfo!");
8053 MessagePortBase* port = nullptr;
8054 nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
8055 if (NS_SUCCEEDED(rv)) {
8056 nsRefPtr<MessagePortBase> newPort;
8057 if (scInfo->ports.Get(port, getter_AddRefs(newPort))) {
8058 // No duplicate.
8059 return false;
8062 newPort = port->Clone();
8063 scInfo->ports.Put(port, newPort);
8065 *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
8066 *aOwnership = JS::SCTAG_TMO_CUSTOM;
8067 *aContent = newPort;
8068 *aExtraData = 0;
8070 return true;
8073 return false;
8076 void
8077 PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
8078 void *aContent, uint64_t aExtraData, void* aClosure)
8080 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
8081 NS_ASSERTION(scInfo, "Must have scInfo!");
8083 if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
8084 nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
8085 scInfo->ports.Remove(port);
8089 JSStructuredCloneCallbacks kPostMessageCallbacks = {
8090 PostMessageReadStructuredClone,
8091 PostMessageWriteStructuredClone,
8092 nullptr,
8093 PostMessageReadTransferStructuredClone,
8094 PostMessageTransferStructuredClone,
8095 PostMessageFreeTransferStructuredClone
8098 } // anonymous namespace
8100 static PLDHashOperator
8101 PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
8103 nsTArray<nsRefPtr<MessagePortBase> > *array =
8104 static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
8106 array->AppendElement(aKey);
8107 return PL_DHASH_NEXT;
8110 NS_IMETHODIMP
8111 PostMessageEvent::Run()
8113 NS_ABORT_IF_FALSE(mTargetWindow->IsOuterWindow(),
8114 "should have been passed an outer window!");
8115 NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(),
8116 "should have been passed an outer window!");
8118 AutoJSAPI jsapi;
8119 jsapi.Init();
8120 JSContext* cx = jsapi.cx();
8122 // If we bailed before this point we're going to leak mMessage, but
8123 // that's probably better than crashing.
8125 nsRefPtr<nsGlobalWindow> targetWindow;
8126 if (mTargetWindow->IsClosedOrClosing() ||
8127 !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
8128 targetWindow->IsClosedOrClosing())
8129 return NS_OK;
8131 NS_ABORT_IF_FALSE(targetWindow->IsInnerWindow(),
8132 "we ordered an inner window!");
8133 JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
8135 // Ensure that any origin which might have been provided is the origin of this
8136 // window's document. Note that we do this *now* instead of when postMessage
8137 // is called because the target window might have been navigated to a
8138 // different location between then and now. If this check happened when
8139 // postMessage was called, it would be fairly easy for a malicious webpage to
8140 // intercept messages intended for another site by carefully timing navigation
8141 // of the target window so it changed location after postMessage but before
8142 // now.
8143 if (mProvidedPrincipal) {
8144 // Get the target's origin either from its principal or, in the case the
8145 // principal doesn't carry a URI (e.g. the system principal), the target's
8146 // document.
8147 nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
8148 if (NS_WARN_IF(!targetPrin))
8149 return NS_OK;
8151 // Note: This is contrary to the spec with respect to file: URLs, which
8152 // the spec groups into a single origin, but given we intentionally
8153 // don't do that in other places it seems better to hold the line for
8154 // now. Long-term, we want HTML5 to address this so that we can
8155 // be compliant while being safer.
8156 if (!targetPrin->Equals(mProvidedPrincipal)) {
8157 return NS_OK;
8161 // Deserialize the structured clone data
8162 JS::Rooted<JS::Value> messageData(cx);
8163 StructuredCloneInfo scInfo;
8164 scInfo.event = this;
8165 scInfo.window = targetWindow;
8167 if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
8168 return NS_ERROR_DOM_DATA_CLONE_ERR;
8171 // Create the event
8172 nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
8173 do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
8174 nsRefPtr<MessageEvent> event =
8175 new MessageEvent(eventTarget, nullptr, nullptr);
8177 event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
8178 false /*cancelable */, messageData, mCallerOrigin,
8179 EmptyString(), mSource);
8181 nsTArray<nsRefPtr<MessagePortBase> > ports;
8182 scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
8183 event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
8185 // We can't simply call dispatchEvent on the window because doing so ends
8186 // up flipping the trusted bit on the event, and we don't want that to
8187 // happen because then untrusted content can call postMessage on a chrome
8188 // window if it can get a reference to it.
8190 nsIPresShell *shell = targetWindow->mDoc->GetShell();
8191 nsRefPtr<nsPresContext> presContext;
8192 if (shell)
8193 presContext = shell->GetPresContext();
8195 event->SetTrusted(mTrustedCaller);
8196 WidgetEvent* internalEvent = event->GetInternalNSEvent();
8198 nsEventStatus status = nsEventStatus_eIgnore;
8199 EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
8200 presContext,
8201 internalEvent,
8202 static_cast<dom::Event*>(event.get()),
8203 &status);
8204 return NS_OK;
8207 void
8208 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
8209 const nsAString& aTargetOrigin,
8210 JS::Handle<JS::Value> aTransfer,
8211 ErrorResult& aError)
8213 FORWARD_TO_OUTER_OR_THROW(PostMessageMoz,
8214 (aCx, aMessage, aTargetOrigin, aTransfer, aError),
8215 aError, );
8218 // Window.postMessage is an intentional subversion of the same-origin policy.
8219 // As such, this code must be particularly careful in the information it
8220 // exposes to calling code.
8222 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
8225 // First, get the caller's window
8226 nsRefPtr<nsGlobalWindow> callerInnerWin = CallerInnerWindow();
8227 nsIPrincipal* callerPrin;
8228 if (callerInnerWin) {
8229 NS_ABORT_IF_FALSE(callerInnerWin->IsInnerWindow(),
8230 "should have gotten an inner window here");
8232 // Compute the caller's origin either from its principal or, in the case the
8233 // principal doesn't carry a URI (e.g. the system principal), the caller's
8234 // document. We must get this now instead of when the event is created and
8235 // dispatched, because ultimately it is the identity of the calling window
8236 // *now* that determines who sent the message (and not an identity which might
8237 // have changed due to intervening navigations).
8238 callerPrin = callerInnerWin->GetPrincipal();
8240 else {
8241 // In case the global is not a window, it can be a sandbox, and the sandbox's
8242 // principal can be used for the security check.
8243 nsIGlobalObject* global = GetIncumbentGlobal();
8244 NS_ASSERTION(global, "Why is there no global object?");
8245 callerPrin = global->PrincipalOrNull();
8247 if (!callerPrin) {
8248 return;
8251 nsCOMPtr<nsIURI> callerOuterURI;
8252 if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) {
8253 return;
8256 nsAutoString origin;
8257 if (callerOuterURI) {
8258 // if the principal has a URI, use that to generate the origin
8259 nsContentUtils::GetUTFOrigin(callerPrin, origin);
8261 else if (callerInnerWin) {
8262 // otherwise use the URI of the document to generate origin
8263 nsCOMPtr<nsIDocument> doc = callerInnerWin->GetExtantDoc();
8264 if (!doc) {
8265 return;
8267 callerOuterURI = doc->GetDocumentURI();
8268 // if the principal has a URI, use that to generate the origin
8269 nsContentUtils::GetUTFOrigin(callerOuterURI, origin);
8271 else {
8272 // in case of a sandbox with a system principal origin can be empty
8273 if (!nsContentUtils::IsSystemPrincipal(callerPrin)) {
8274 return;
8278 // Convert the provided origin string into a URI for comparison purposes.
8279 nsCOMPtr<nsIPrincipal> providedPrincipal;
8281 if (aTargetOrigin.EqualsASCII("/")) {
8282 providedPrincipal = GetEntryGlobal()->PrincipalOrNull();
8283 if (NS_WARN_IF(!providedPrincipal))
8284 return;
8287 // "*" indicates no specific origin is required.
8288 else if (!aTargetOrigin.EqualsASCII("*")) {
8289 nsCOMPtr<nsIURI> originURI;
8290 if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
8291 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
8292 return;
8295 if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
8296 NS_FAILED(originURI->SetPath(EmptyCString()))) {
8297 return;
8300 nsCOMPtr<nsIScriptSecurityManager> ssm =
8301 nsContentUtils::GetSecurityManager();
8302 MOZ_ASSERT(ssm);
8304 nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipal();
8305 MOZ_ASSERT(principal);
8307 uint32_t appId;
8308 if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId))))
8309 return;
8311 bool isInBrowser;
8312 if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser))))
8313 return;
8315 // Create a nsIPrincipal inheriting the app/browser attributes from the
8316 // caller.
8317 nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
8318 getter_AddRefs(providedPrincipal));
8319 if (NS_WARN_IF(NS_FAILED(rv))) {
8320 return;
8324 // Create and asynchronously dispatch a runnable which will handle actual DOM
8325 // event creation and dispatch.
8326 nsRefPtr<PostMessageEvent> event =
8327 new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
8328 ? nullptr
8329 : callerInnerWin->GetOuterWindowInternal(),
8330 origin,
8331 this,
8332 providedPrincipal,
8333 nsContentUtils::IsCallerChrome());
8335 // We *must* clone the data here, or the JS::Value could be modified
8336 // by script
8337 StructuredCloneInfo scInfo;
8338 scInfo.event = event;
8339 scInfo.window = this;
8341 nsIPrincipal* principal = GetPrincipal();
8342 JS::Rooted<JS::Value> message(aCx, aMessage);
8343 JS::Rooted<JS::Value> transfer(aCx, aTransfer);
8344 if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)) ||
8345 !event->Buffer().write(aCx, message, transfer, &kPostMessageCallbacks,
8346 &scInfo)) {
8347 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
8348 return;
8351 aError = NS_DispatchToCurrentThread(event);
8354 void
8355 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
8356 const nsAString& aTargetOrigin,
8357 const Optional<Sequence<JS::Value > >& aTransfer,
8358 ErrorResult& aError)
8360 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
8361 if (aTransfer.WasPassed()) {
8362 const Sequence<JS::Value >& values = aTransfer.Value();
8364 // The input sequence only comes from the generated bindings code, which
8365 // ensures it is rooted.
8366 JS::HandleValueArray elements =
8367 JS::HandleValueArray::fromMarkedLocation(values.Length(), values.Elements());
8369 transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements));
8370 if (transferArray.isNull()) {
8371 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
8372 return;
8376 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aError);
8379 NS_IMETHODIMP
8380 nsGlobalWindow::PostMessageMoz(JS::Handle<JS::Value> aMessage,
8381 const nsAString& aOrigin,
8382 JS::Handle<JS::Value> aTransfer,
8383 JSContext* aCx)
8385 ErrorResult rv;
8386 PostMessageMoz(aCx, aMessage, aOrigin, aTransfer, rv);
8388 return rv.ErrorCode();
8391 class nsCloseEvent : public nsRunnable {
8393 nsRefPtr<nsGlobalWindow> mWindow;
8394 bool mIndirect;
8396 nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect)
8397 : mWindow(aWindow)
8398 , mIndirect(aIndirect)
8401 public:
8403 static nsresult
8404 PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) {
8405 nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
8406 nsresult rv = NS_DispatchToCurrentThread(ev);
8407 if (NS_SUCCEEDED(rv))
8408 aWindow->MaybeForgiveSpamCount();
8409 return rv;
8412 NS_IMETHOD Run() {
8413 if (mWindow) {
8414 if (mIndirect) {
8415 return PostCloseEvent(mWindow, false);
8417 mWindow->ReallyCloseWindow();
8419 return NS_OK;
8424 bool
8425 nsGlobalWindow::CanClose()
8427 MOZ_ASSERT(IsOuterWindow());
8429 if (!mDocShell) {
8430 return true;
8433 // Ask the content viewer whether the toplevel window can close.
8434 // If the content viewer returns false, it is responsible for calling
8435 // Close() as soon as it is possible for the window to close.
8436 // This allows us to not close the window while printing is happening.
8438 nsCOMPtr<nsIContentViewer> cv;
8439 mDocShell->GetContentViewer(getter_AddRefs(cv));
8440 if (cv) {
8441 bool canClose;
8442 nsresult rv = cv->PermitUnload(false, &canClose);
8443 if (NS_SUCCEEDED(rv) && !canClose)
8444 return false;
8446 rv = cv->RequestWindowClose(&canClose);
8447 if (NS_SUCCEEDED(rv) && !canClose)
8448 return false;
8451 return true;
8454 void
8455 nsGlobalWindow::Close(ErrorResult& aError)
8457 FORWARD_TO_OUTER_OR_THROW(Close, (aError), aError, );
8459 if (!mDocShell || IsInModalState() ||
8460 (IsFrame() && !mDocShell->GetIsBrowserOrApp())) {
8461 // window.close() is called on a frame in a frameset, on a window
8462 // that's already closed, or on a window for which there's
8463 // currently a modal dialog open. Ignore such calls.
8464 return;
8467 if (mHavePendingClose) {
8468 // We're going to be closed anyway; do nothing since we don't want
8469 // to double-close
8470 return;
8473 if (mBlockScriptedClosingFlag)
8475 // A script's popup has been blocked and we don't want
8476 // the window to be closed directly after this event,
8477 // so the user can see that there was a blocked popup.
8478 return;
8481 // Don't allow scripts from content to close non-app or non-neterror
8482 // windows that were not opened by script.
8483 nsAutoString url;
8484 mDoc->GetURL(url);
8485 if (!mDocShell->GetIsApp() &&
8486 !StringBeginsWith(url, NS_LITERAL_STRING("about:neterror")) &&
8487 !mHadOriginalOpener && !nsContentUtils::IsCallerChrome()) {
8488 bool allowClose = mAllowScriptsToClose ||
8489 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
8490 if (!allowClose) {
8491 // We're blocking the close operation
8492 // report localized error msg in JS console
8493 nsContentUtils::ReportToConsole(
8494 nsIScriptError::warningFlag,
8495 NS_LITERAL_CSTRING("DOM Window"), mDoc, // Better name for the category?
8496 nsContentUtils::eDOM_PROPERTIES,
8497 "WindowCloseBlockedWarning");
8499 return;
8503 if (!mInClose && !mIsClosed && !CanClose()) {
8504 return;
8507 // Fire a DOM event notifying listeners that this window is about to
8508 // be closed. The tab UI code may choose to cancel the default
8509 // action for this event, if so, we won't actually close the window
8510 // (since the tab UI code will close the tab in stead). Sure, this
8511 // could be abused by content code, but do we care? I don't think
8512 // so...
8514 bool wasInClose = mInClose;
8515 mInClose = true;
8517 if (!DispatchCustomEvent(NS_LITERAL_STRING("DOMWindowClose"))) {
8518 // Someone chose to prevent the default action for this event, if
8519 // so, let's not close this window after all...
8521 mInClose = wasInClose;
8522 return;
8525 FinalClose();
8528 NS_IMETHODIMP
8529 nsGlobalWindow::Close()
8531 ErrorResult rv;
8532 Close(rv);
8534 return rv.ErrorCode();
8537 void
8538 nsGlobalWindow::ForceClose()
8540 MOZ_ASSERT(IsOuterWindow());
8542 if (IsFrame() || !mDocShell) {
8543 // This may be a frame in a frameset, or a window that's already closed.
8544 // Ignore such calls.
8545 return;
8548 if (mHavePendingClose) {
8549 // We're going to be closed anyway; do nothing since we don't want
8550 // to double-close
8551 return;
8554 mInClose = true;
8556 DispatchCustomEvent(NS_LITERAL_STRING("DOMWindowClose"));
8558 FinalClose();
8561 void
8562 nsGlobalWindow::FinalClose()
8564 MOZ_ASSERT(IsOuterWindow());
8566 // Flag that we were closed.
8567 mIsClosed = true;
8569 // This stuff is non-sensical but incredibly fragile. The reasons for the
8570 // behavior here don't make sense today and may not have ever made sense,
8571 // but various bits of frontend code break when you change them. If you need
8572 // to fix up this behavior, feel free to. It's a righteous task, but involves
8573 // wrestling with various download manager tests, frontend code, and possible
8574 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
8575 // testing ground.
8577 // In particular, if |win|'s JSContext is at the top of the stack, we must
8578 // complete _two_ round-trips to the event loop before the call to
8579 // ReallyCloseWindow. This allows setTimeout handlers that are set after
8580 // FinalClose() is called to run before the window is torn down.
8581 bool indirect = GetContextInternal() && // Occasionally null. See bug 877390.
8582 (nsContentUtils::GetCurrentJSContext() ==
8583 GetContextInternal()->GetNativeContext());
8584 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
8585 ReallyCloseWindow();
8586 } else {
8587 mHavePendingClose = true;
8592 void
8593 nsGlobalWindow::ReallyCloseWindow()
8595 FORWARD_TO_OUTER_VOID(ReallyCloseWindow, ());
8597 // Make sure we never reenter this method.
8598 mHavePendingClose = true;
8600 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
8602 // If there's no treeOwnerAsWin, this window must already be closed.
8604 if (treeOwnerAsWin) {
8606 // but if we're a browser window we could be in some nasty
8607 // self-destroying cascade that we should mostly ignore
8609 if (mDocShell) {
8610 nsCOMPtr<nsIBrowserDOMWindow> bwin;
8611 nsCOMPtr<nsIDocShellTreeItem> rootItem;
8612 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
8613 nsCOMPtr<nsIDOMWindow> rootWin =
8614 rootItem ? rootItem->GetWindow() : nullptr;
8615 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
8616 if (chromeWin)
8617 chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
8619 if (rootWin) {
8620 /* Normally we destroy the entire window, but not if
8621 this DOM window belongs to a tabbed browser and doesn't
8622 correspond to a tab. This allows a well-behaved tab
8623 to destroy the container as it should but is a final measure
8624 to prevent an errant tab from doing so when it shouldn't.
8625 This works because we reach this code when we shouldn't only
8626 in the particular circumstance that we belong to a tab
8627 that has just been closed (and is therefore already missing
8628 from the list of browsers) (and has an unload handler
8629 that closes the window). */
8630 // XXXbz now that we have mHavePendingClose, is this needed?
8631 bool isTab = false;
8632 if (rootWin == this ||
8633 !bwin || (bwin->IsTabContentWindow(GetOuterWindowInternal(),
8634 &isTab), isTab))
8635 treeOwnerAsWin->Destroy();
8639 CleanUp();
8643 void
8644 nsGlobalWindow::EnterModalState()
8646 MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
8648 // GetScriptableTop, not GetTop, so that EnterModalState works properly with
8649 // <iframe mozbrowser>.
8650 nsGlobalWindow* topWin = GetScriptableTop();
8652 if (!topWin) {
8653 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
8654 return;
8657 // If there is an active ESM in this window, clear it. Otherwise, this can
8658 // cause a problem if a modal state is entered during a mouseup event.
8659 EventStateManager* activeESM =
8660 static_cast<EventStateManager*>(
8661 EventStateManager::GetActiveEventStateManager());
8662 if (activeESM && activeESM->GetPresContext()) {
8663 nsIPresShell* activeShell = activeESM->GetPresContext()->GetPresShell();
8664 if (activeShell && (
8665 nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(), mDoc) ||
8666 nsContentUtils::ContentIsCrossDocDescendantOf(mDoc, activeShell->GetDocument()))) {
8667 EventStateManager::ClearGlobalActiveContent(activeESM);
8669 activeShell->SetCapturingContent(nullptr, 0);
8671 if (activeShell) {
8672 nsRefPtr<nsFrameSelection> frameSelection = activeShell->FrameSelection();
8673 frameSelection->SetDragState(false);
8678 // If there are any drag and drop operations in flight, try to end them.
8679 nsCOMPtr<nsIDragService> ds =
8680 do_GetService("@mozilla.org/widget/dragservice;1");
8681 if (ds) {
8682 ds->EndDragSession(true);
8685 // Clear the capturing content if it is under topDoc.
8686 // Usually the activeESM check above does that, but there are cases when
8687 // we don't have activeESM, or it is for different document.
8688 nsIDocument* topDoc = topWin->GetExtantDoc();
8689 nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
8690 if (capturingContent && topDoc &&
8691 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
8692 nsIPresShell::SetCapturingContent(nullptr, 0);
8695 if (topWin->mModalStateDepth == 0) {
8696 NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
8698 mSuspendedDoc = topDoc;
8699 if (mSuspendedDoc) {
8700 mSuspendedDoc->SuppressEventHandling(nsIDocument::eAnimationsOnly);
8703 topWin->mModalStateDepth++;
8706 // static
8707 void
8708 nsGlobalWindow::RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow,
8709 nsGlobalWindow *aWindow)
8711 nsGlobalWindow *inner;
8713 // Return early if we're frozen or have no inner window.
8714 if (!(inner = aWindow->GetCurrentInnerWindowInternal()) ||
8715 inner->IsFrozen()) {
8716 return;
8719 inner->RunTimeout(nullptr);
8721 // Check again if we're frozen since running pending timeouts
8722 // could've frozen us.
8723 if (inner->IsFrozen()) {
8724 return;
8727 nsCOMPtr<nsIDOMWindowCollection> frames;
8728 aWindow->GetFrames(getter_AddRefs(frames));
8730 if (!frames) {
8731 return;
8734 uint32_t i, length;
8735 if (NS_FAILED(frames->GetLength(&length)) || !length) {
8736 return;
8739 for (i = 0; i < length && aTopWindow->mModalStateDepth == 0; i++) {
8740 nsCOMPtr<nsIDOMWindow> child;
8741 frames->Item(i, getter_AddRefs(child));
8743 if (!child) {
8744 return;
8747 nsGlobalWindow *childWin =
8748 static_cast<nsGlobalWindow *>
8749 (static_cast<nsIDOMWindow *>
8750 (child.get()));
8752 RunPendingTimeoutsRecursive(aTopWindow, childWin);
8756 class nsPendingTimeoutRunner : public nsRunnable
8758 public:
8759 explicit nsPendingTimeoutRunner(nsGlobalWindow* aWindow)
8760 : mWindow(aWindow)
8762 NS_ASSERTION(mWindow, "mWindow is null.");
8765 NS_IMETHOD Run()
8767 nsGlobalWindow::RunPendingTimeoutsRecursive(mWindow, mWindow);
8769 return NS_OK;
8772 private:
8773 nsRefPtr<nsGlobalWindow> mWindow;
8776 void
8777 nsGlobalWindow::LeaveModalState()
8779 MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
8781 nsGlobalWindow* topWin = GetScriptableTop();
8783 if (!topWin) {
8784 NS_ERROR("Uh, LeaveModalState() called w/o a reachable top window?");
8785 return;
8788 topWin->mModalStateDepth--;
8790 if (topWin->mModalStateDepth == 0) {
8791 nsCOMPtr<nsIRunnable> runner = new nsPendingTimeoutRunner(topWin);
8792 if (NS_FAILED(NS_DispatchToCurrentThread(runner)))
8793 NS_WARNING("failed to dispatch pending timeout runnable");
8795 if (mSuspendedDoc) {
8796 nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc();
8797 mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eAnimationsOnly,
8798 currentDoc == mSuspendedDoc);
8799 mSuspendedDoc = nullptr;
8803 // Remember the time of the last dialog quit.
8804 nsGlobalWindow *inner = topWin->GetCurrentInnerWindowInternal();
8805 if (inner)
8806 inner->mLastDialogQuitTime = TimeStamp::Now();
8809 bool
8810 nsGlobalWindow::IsInModalState()
8812 nsGlobalWindow *topWin = GetScriptableTop();
8814 if (!topWin) {
8815 NS_ERROR("Uh, IsInModalState() called w/o a reachable top window?");
8817 return false;
8820 return topWin->mModalStateDepth != 0;
8823 // static
8824 void
8825 nsGlobalWindow::NotifyDOMWindowDestroyed(nsGlobalWindow* aWindow) {
8826 nsCOMPtr<nsIObserverService> observerService =
8827 services::GetObserverService();
8828 if (observerService) {
8829 observerService->
8830 NotifyObservers(ToSupports(aWindow),
8831 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
8835 class WindowDestroyedEvent : public nsRunnable
8837 public:
8838 WindowDestroyedEvent(nsPIDOMWindow* aWindow, uint64_t aID,
8839 const char* aTopic) :
8840 mID(aID), mTopic(aTopic)
8842 mWindow = do_GetWeakReference(aWindow);
8845 NS_IMETHOD Run()
8847 nsCOMPtr<nsIObserverService> observerService =
8848 do_GetService("@mozilla.org/observer-service;1");
8849 if (observerService) {
8850 nsCOMPtr<nsISupportsPRUint64> wrapper =
8851 do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
8852 if (wrapper) {
8853 wrapper->SetData(mID);
8854 observerService->NotifyObservers(wrapper, mTopic.get(), nullptr);
8858 bool skipNukeCrossCompartment = false;
8859 #ifndef DEBUG
8860 nsCOMPtr<nsIAppStartup> appStartup =
8861 do_GetService(NS_APPSTARTUP_CONTRACTID);
8863 if (appStartup) {
8864 appStartup->GetShuttingDown(&skipNukeCrossCompartment);
8866 #endif
8868 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
8869 if (!skipNukeCrossCompartment && window) {
8870 nsGlobalWindow* currentInner =
8871 window->IsInnerWindow() ? static_cast<nsGlobalWindow*>(window.get()) :
8872 static_cast<nsGlobalWindow*>(window->GetCurrentInnerWindow());
8873 NS_ENSURE_TRUE(currentInner, NS_OK);
8875 AutoSafeJSContext cx;
8876 JS::Rooted<JSObject*> obj(cx, currentInner->FastGetGlobalJSObject());
8877 // We only want to nuke wrappers for the chrome->content case
8878 if (obj && !js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
8879 js::NukeCrossCompartmentWrappers(cx,
8880 js::ChromeCompartmentsOnly(),
8881 js::SingleCompartment(js::GetObjectCompartment(obj)),
8882 window->IsInnerWindow() ? js::DontNukeWindowReferences :
8883 js::NukeWindowReferences);
8887 return NS_OK;
8890 private:
8891 uint64_t mID;
8892 nsCString mTopic;
8893 nsWeakPtr mWindow;
8896 void
8897 nsGlobalWindow::NotifyWindowIDDestroyed(const char* aTopic)
8899 nsRefPtr<nsIRunnable> runnable = new WindowDestroyedEvent(this, mWindowID, aTopic);
8900 nsresult rv = NS_DispatchToCurrentThread(runnable);
8901 if (NS_SUCCEEDED(rv)) {
8902 mNotifiedIDDestroyed = true;
8906 // static
8907 void
8908 nsGlobalWindow::NotifyDOMWindowFrozen(nsGlobalWindow* aWindow) {
8909 if (aWindow && aWindow->IsInnerWindow()) {
8910 nsCOMPtr<nsIObserverService> observerService =
8911 services::GetObserverService();
8912 if (observerService) {
8913 observerService->
8914 NotifyObservers(ToSupports(aWindow),
8915 DOM_WINDOW_FROZEN_TOPIC, nullptr);
8920 // static
8921 void
8922 nsGlobalWindow::NotifyDOMWindowThawed(nsGlobalWindow* aWindow) {
8923 if (aWindow && aWindow->IsInnerWindow()) {
8924 nsCOMPtr<nsIObserverService> observerService =
8925 services::GetObserverService();
8926 if (observerService) {
8927 observerService->
8928 NotifyObservers(ToSupports(aWindow),
8929 DOM_WINDOW_THAWED_TOPIC, nullptr);
8934 JSObject*
8935 nsGlobalWindow::GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey)
8937 AutoSafeJSContext cx;
8938 JS::Rooted<JSObject*> handler(cx);
8939 if (mCachedXBLPrototypeHandlers) {
8940 mCachedXBLPrototypeHandlers->Get(aKey, handler.address());
8942 return handler;
8945 void
8946 nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
8947 JS::Handle<JSObject*> aHandler)
8949 if (!mCachedXBLPrototypeHandlers) {
8950 mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
8951 PreserveWrapper(ToSupports(this));
8954 mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
8958 * GetScriptableFrameElement is called when script reads
8959 * nsIGlobalWindow::frameElement.
8961 * In contrast to GetRealFrameElement, GetScriptableFrameElement says that the
8962 * window contained by an <iframe mozbrowser> or <iframe mozapp> has no frame
8963 * element (effectively treating a mozbrowser the same as a content/chrome
8964 * boundary).
8966 NS_IMETHODIMP
8967 nsGlobalWindow::GetScriptableFrameElement(nsIDOMElement** aFrameElement)
8969 ErrorResult rv;
8970 nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(GetFrameElement(rv));
8971 if (rv.Failed()) {
8972 return rv.ErrorCode();
8975 frameElement.forget(aFrameElement);
8977 return NS_OK;
8980 Element*
8981 nsGlobalWindow::GetFrameElement(ErrorResult& aError)
8983 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aError), aError, nullptr);
8985 if (!mDocShell || mDocShell->GetIsBrowserOrApp()) {
8986 return nullptr;
8989 // Per HTML5, the frameElement getter returns null in cross-origin situations.
8990 Element* element = GetRealFrameElement(aError);
8991 if (aError.Failed() || !element) {
8992 return nullptr;
8994 if (!nsContentUtils::SubjectPrincipal()->
8995 SubsumesConsideringDomain(element->NodePrincipal())) {
8996 return nullptr;
8998 return element;
9001 Element*
9002 nsGlobalWindow::GetRealFrameElement(ErrorResult& aError)
9004 FORWARD_TO_OUTER_OR_THROW(GetRealFrameElement, (aError), aError, nullptr);
9006 if (!mDocShell) {
9007 return nullptr;
9010 nsCOMPtr<nsIDocShell> parent;
9011 mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
9013 if (!parent || parent == mDocShell) {
9014 // We're at a chrome boundary, don't expose the chrome iframe
9015 // element to content code.
9016 return nullptr;
9019 return mFrameElement;
9023 * nsIGlobalWindow::GetFrameElement (when called from C++) is just a wrapper
9024 * around GetRealFrameElement.
9026 NS_IMETHODIMP
9027 nsGlobalWindow::GetRealFrameElement(nsIDOMElement** aFrameElement)
9029 ErrorResult rv;
9030 nsCOMPtr<nsIDOMElement> frameElement =
9031 do_QueryInterface(GetRealFrameElement(rv));
9032 frameElement.forget(aFrameElement);
9034 return rv.ErrorCode();
9037 // Helper for converting window.showModalDialog() options (list of ';'
9038 // separated name (:|=) value pairs) to a format that's parsable by
9039 // our normal window opening code.
9041 void
9042 ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult)
9044 nsAString::const_iterator end;
9045 aOptions.EndReading(end);
9047 nsAString::const_iterator iter;
9048 aOptions.BeginReading(iter);
9050 while (iter != end) {
9051 // Skip whitespace.
9052 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
9053 ++iter;
9056 nsAString::const_iterator name_start = iter;
9058 // Skip characters until we find whitespace, ';', ':', or '='
9059 while (iter != end && !nsCRT::IsAsciiSpace(*iter) &&
9060 *iter != ';' &&
9061 *iter != ':' &&
9062 *iter != '=') {
9063 ++iter;
9066 nsAString::const_iterator name_end = iter;
9068 // Skip whitespace.
9069 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
9070 ++iter;
9073 if (*iter == ';') {
9074 // No value found, skip the ';' and keep going.
9075 ++iter;
9077 continue;
9080 nsAString::const_iterator value_start = iter;
9081 nsAString::const_iterator value_end = iter;
9083 if (*iter == ':' || *iter == '=') {
9084 // We found name followed by ':' or '='. Look for a value.
9086 iter++; // Skip the ':' or '='
9088 // Skip whitespace.
9089 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
9090 ++iter;
9093 value_start = iter;
9095 // Skip until we find whitespace, or ';'.
9096 while (iter != end && !nsCRT::IsAsciiSpace(*iter) &&
9097 *iter != ';') {
9098 ++iter;
9101 value_end = iter;
9103 // Skip whitespace.
9104 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
9105 ++iter;
9109 const nsDependentSubstring& name = Substring(name_start, name_end);
9110 const nsDependentSubstring& value = Substring(value_start, value_end);
9112 if (name.LowerCaseEqualsLiteral("center")) {
9113 if (value.LowerCaseEqualsLiteral("on") ||
9114 value.LowerCaseEqualsLiteral("yes") ||
9115 value.LowerCaseEqualsLiteral("1")) {
9116 aResult.AppendLiteral(",centerscreen=1");
9118 } else if (name.LowerCaseEqualsLiteral("dialogwidth")) {
9119 if (!value.IsEmpty()) {
9120 aResult.AppendLiteral(",width=");
9121 aResult.Append(value);
9123 } else if (name.LowerCaseEqualsLiteral("dialogheight")) {
9124 if (!value.IsEmpty()) {
9125 aResult.AppendLiteral(",height=");
9126 aResult.Append(value);
9128 } else if (name.LowerCaseEqualsLiteral("dialogtop")) {
9129 if (!value.IsEmpty()) {
9130 aResult.AppendLiteral(",top=");
9131 aResult.Append(value);
9133 } else if (name.LowerCaseEqualsLiteral("dialogleft")) {
9134 if (!value.IsEmpty()) {
9135 aResult.AppendLiteral(",left=");
9136 aResult.Append(value);
9138 } else if (name.LowerCaseEqualsLiteral("resizable")) {
9139 if (value.LowerCaseEqualsLiteral("on") ||
9140 value.LowerCaseEqualsLiteral("yes") ||
9141 value.LowerCaseEqualsLiteral("1")) {
9142 aResult.AppendLiteral(",resizable=1");
9144 } else if (name.LowerCaseEqualsLiteral("scroll")) {
9145 if (value.LowerCaseEqualsLiteral("off") ||
9146 value.LowerCaseEqualsLiteral("no") ||
9147 value.LowerCaseEqualsLiteral("0")) {
9148 aResult.AppendLiteral(",scrollbars=0");
9152 if (iter == end) {
9153 break;
9156 iter++;
9160 already_AddRefed<nsIVariant>
9161 nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
9162 const nsAString& aOptions, ErrorResult& aError)
9164 if (mDoc) {
9165 mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog);
9168 FORWARD_TO_OUTER_OR_THROW(ShowModalDialog,
9169 (aUrl, aArgument, aOptions, aError), aError,
9170 nullptr);
9172 if (!IsShowModalDialogEnabled()) {
9173 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9174 return nullptr;
9177 nsRefPtr<DialogValueHolder> argHolder =
9178 new DialogValueHolder(nsContentUtils::SubjectPrincipal(), aArgument);
9180 // Before bringing up the window/dialog, unsuppress painting and flush
9181 // pending reflows.
9182 EnsureReflowFlushAndPaint();
9184 if (!AreDialogsEnabled()) {
9185 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9186 return nullptr;
9189 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
9190 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9191 return nullptr;
9194 nsCOMPtr<nsIDOMWindow> dlgWin;
9195 nsAutoString options(NS_LITERAL_STRING("-moz-internal-modal=1,status=1"));
9197 ConvertDialogOptions(aOptions, options);
9199 options.AppendLiteral(",scrollbars=1,centerscreen=1,resizable=0");
9201 EnterModalState();
9202 uint32_t oldMicroTaskLevel = nsContentUtils::MicroTaskLevel();
9203 nsContentUtils::SetMicroTaskLevel(0);
9204 aError = OpenInternal(aUrl, EmptyString(), options,
9205 false, // aDialog
9206 true, // aContentModal
9207 true, // aCalledNoScript
9208 true, // aDoJSFixups
9209 true, // aNavigate
9210 nullptr, argHolder, // args
9211 GetPrincipal(), // aCalleePrincipal
9212 nullptr, // aJSCallerContext
9213 getter_AddRefs(dlgWin));
9214 nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel);
9215 LeaveModalState();
9216 if (aError.Failed()) {
9217 return nullptr;
9220 nsCOMPtr<nsIDOMModalContentWindow> dialog = do_QueryInterface(dlgWin);
9221 if (!dialog) {
9222 return nullptr;
9225 nsCOMPtr<nsIVariant> retVal;
9226 aError = dialog->GetReturnValue(getter_AddRefs(retVal));
9227 MOZ_ASSERT(!aError.Failed());
9229 return retVal.forget();
9232 void
9233 nsGlobalWindow::ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
9234 JS::Handle<JS::Value> aArgument,
9235 const nsAString& aOptions,
9236 JS::MutableHandle<JS::Value> aRetval,
9237 ErrorResult& aError)
9239 nsCOMPtr<nsIVariant> args;
9240 aError = nsContentUtils::XPConnect()->JSToVariant(aCx,
9241 aArgument,
9242 getter_AddRefs(args));
9243 if (aError.Failed()) {
9244 return;
9247 nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aUrl, args, aOptions, aError);
9248 if (aError.Failed()) {
9249 return;
9252 JS::Rooted<JS::Value> result(aCx);
9253 if (retVal) {
9254 aError = nsContentUtils::XPConnect()->VariantToJS(aCx,
9255 FastGetGlobalJSObject(),
9256 retVal, aRetval);
9257 } else {
9258 aRetval.setNull();
9262 NS_IMETHODIMP
9263 nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs_,
9264 const nsAString& aOptions, uint8_t aArgc,
9265 nsIVariant **aRetVal)
9267 // Per-spec the |arguments| parameter is supposed to pass through unmodified.
9268 // However, XPConnect default-initializes variants to null, rather than
9269 // undefined. Fix this up here.
9270 nsCOMPtr<nsIVariant> aArgs = aArgs_;
9271 if (aArgc < 1) {
9272 aArgs = CreateVoidVariant();
9275 ErrorResult rv;
9276 nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aURI, aArgs, aOptions, rv);
9277 retVal.forget(aRetVal);
9279 return rv.ErrorCode();
9282 class CommandDispatcher : public nsRunnable
9284 public:
9285 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
9286 const nsAString& aAction)
9287 : mDispatcher(aDispatcher), mAction(aAction) {}
9289 NS_IMETHOD Run()
9291 return mDispatcher->UpdateCommands(mAction);
9294 nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
9295 nsString mAction;
9298 static bool
9299 CheckReason(int16_t aReason, SelectionChangeReason aReasonType)
9301 switch (aReasonType) {
9302 case SelectionChangeReason::Drag:
9303 return aReason & nsISelectionListener::DRAG_REASON;
9304 case SelectionChangeReason::Mousedown:
9305 return aReason & nsISelectionListener::MOUSEDOWN_REASON;
9306 case SelectionChangeReason::Mouseup:
9307 return aReason & nsISelectionListener::MOUSEUP_REASON;
9308 case SelectionChangeReason::Keypress:
9309 return aReason & nsISelectionListener::KEYPRESS_REASON;
9310 case SelectionChangeReason::Selectall:
9311 return aReason & nsISelectionListener::SELECTALL_REASON;
9312 case SelectionChangeReason::Collapsetostart:
9313 return aReason & nsISelectionListener::COLLAPSETOSTART_REASON;
9314 case SelectionChangeReason::Collapsetoend:
9315 return aReason & nsISelectionListener::COLLAPSETOEND_REASON;
9316 default:
9317 return false;
9321 NS_IMETHODIMP
9322 nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason)
9324 nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
9325 if (!rootWindow)
9326 return NS_OK;
9328 nsCOMPtr<nsIDOMXULDocument> xulDoc =
9329 do_QueryInterface(rootWindow->GetExtantDoc());
9330 // See if we contain a XUL document.
9331 // selectionchange action is only used for mozbrowser, not for XUL. So we bypass
9332 // XUL command dispatch if anAction is "selectionchange".
9333 if (xulDoc && !anAction.EqualsLiteral("selectionchange")) {
9334 // Retrieve the command dispatcher and call updateCommands on it.
9335 nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher;
9336 xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher));
9337 if (xulCommandDispatcher) {
9338 nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher,
9339 anAction));
9343 if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
9344 SelectionChangeEventInit init;
9345 init.mBubbles = true;
9346 if (aSel) {
9347 nsCOMPtr<nsIDOMRange> range;
9348 nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
9349 if (NS_SUCCEEDED(rv) && range) {
9350 nsRefPtr<nsRange> nsrange = static_cast<nsRange*>(range.get());
9351 init.mBoundingClientRect = nsrange->GetBoundingClientRect(true, false);
9352 range->ToString(init.mSelectedText);
9354 for (uint32_t reasonType = 0;
9355 reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
9356 ++reasonType) {
9357 SelectionChangeReason strongReasonType =
9358 static_cast<SelectionChangeReason>(reasonType);
9359 if (CheckReason(aReason, strongReasonType)) {
9360 init.mReasons.AppendElement(strongReasonType);
9365 nsRefPtr<SelectionChangeEvent> event =
9366 SelectionChangeEvent::Constructor(mDoc, NS_LITERAL_STRING("mozselectionchange"), init);
9368 event->SetTrusted(true);
9369 event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
9370 bool ret;
9371 mDoc->DispatchEvent(event, &ret);
9375 return NS_OK;
9378 Selection*
9379 nsGlobalWindow::GetSelection(ErrorResult& aError)
9381 FORWARD_TO_OUTER_OR_THROW(GetSelection, (aError), aError, nullptr);
9383 if (!mDocShell) {
9384 return nullptr;
9387 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
9388 if (!presShell) {
9389 return nullptr;
9392 return static_cast<Selection*>(presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL));
9395 NS_IMETHODIMP
9396 nsGlobalWindow::GetSelection(nsISelection** aSelection)
9398 ErrorResult rv;
9399 nsCOMPtr<nsISelection> selection = GetSelection(rv);
9400 selection.forget(aSelection);
9402 return rv.ErrorCode();
9405 bool
9406 nsGlobalWindow::Find(const nsAString& aString, bool aCaseSensitive,
9407 bool aBackwards, bool aWrapAround, bool aWholeWord,
9408 bool aSearchInFrames, bool aShowDialog,
9409 ErrorResult& aError)
9411 if (Preferences::GetBool("dom.disable_window_find", false)) {
9412 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9413 return false;
9416 FORWARD_TO_OUTER_OR_THROW(Find,
9417 (aString, aCaseSensitive, aBackwards, aWrapAround,
9418 aWholeWord, aSearchInFrames, aShowDialog, aError),
9419 aError, false);
9421 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
9422 if (!finder) {
9423 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9424 return false;
9427 // Set the options of the search
9428 aError = finder->SetSearchString(PromiseFlatString(aString).get());
9429 if (aError.Failed()) {
9430 return false;
9432 finder->SetMatchCase(aCaseSensitive);
9433 finder->SetFindBackwards(aBackwards);
9434 finder->SetWrapFind(aWrapAround);
9435 finder->SetEntireWord(aWholeWord);
9436 finder->SetSearchFrames(aSearchInFrames);
9438 // the nsIWebBrowserFind is initialized to use this window
9439 // as the search root, but uses focus to set the current search
9440 // frame. If we're being called from JS (as here), this window
9441 // should be the current search frame.
9442 nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
9443 if (framesFinder) {
9444 framesFinder->SetRootSearchFrame(this); // paranoia
9445 framesFinder->SetCurrentSearchFrame(this);
9448 // The Find API does not accept empty strings. Launch the Find Dialog.
9449 if (aString.IsEmpty() || aShowDialog) {
9450 // See if the find dialog is already up using nsIWindowMediator
9451 nsCOMPtr<nsIWindowMediator> windowMediator =
9452 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
9454 nsCOMPtr<nsIDOMWindow> findDialog;
9456 if (windowMediator) {
9457 windowMediator->GetMostRecentWindow(MOZ_UTF16("findInPage"),
9458 getter_AddRefs(findDialog));
9461 if (findDialog) {
9462 // The Find dialog is already open, bring it to the top.
9463 aError = findDialog->Focus();
9464 } else if (finder) {
9465 // Open a Find dialog
9466 nsCOMPtr<nsIDOMWindow> dialog;
9467 aError = OpenDialog(NS_LITERAL_STRING("chrome://global/content/finddialog.xul"),
9468 NS_LITERAL_STRING("_blank"),
9469 NS_LITERAL_STRING("chrome, resizable=no, dependent=yes"),
9470 finder, getter_AddRefs(dialog));
9473 return false;
9476 // Launch the search with the passed in search string
9477 bool didFind = false;
9478 aError = finder->FindNext(&didFind);
9479 return didFind;
9482 NS_IMETHODIMP
9483 nsGlobalWindow::Find(const nsAString& aStr, bool aCaseSensitive,
9484 bool aBackwards, bool aWrapAround, bool aWholeWord,
9485 bool aSearchInFrames, bool aShowDialog,
9486 bool *aDidFind)
9488 ErrorResult rv;
9489 *aDidFind = Find(aStr, aCaseSensitive, aBackwards, aWrapAround, aWholeWord,
9490 aSearchInFrames, aShowDialog, rv);
9492 return rv.ErrorCode();
9495 void
9496 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
9497 nsAString& aBinaryData, ErrorResult& aError)
9499 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
9502 NS_IMETHODIMP
9503 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
9504 nsAString& aBinaryData)
9506 ErrorResult rv;
9507 Atob(aAsciiBase64String, aBinaryData, rv);
9509 return rv.ErrorCode();
9512 void
9513 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
9514 nsAString& aAsciiBase64String, ErrorResult& aError)
9516 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
9519 NS_IMETHODIMP
9520 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
9521 nsAString& aAsciiBase64String)
9523 ErrorResult rv;
9524 Btoa(aBinaryData, aAsciiBase64String, rv);
9526 return rv.ErrorCode();
9529 //*****************************************************************************
9530 // nsGlobalWindow::nsIDOMEventTarget
9531 //*****************************************************************************
9533 NS_IMETHODIMP
9534 nsGlobalWindow::RemoveEventListener(const nsAString& aType,
9535 nsIDOMEventListener* aListener,
9536 bool aUseCapture)
9538 if (nsRefPtr<EventListenerManager> elm = GetExistingListenerManager()) {
9539 elm->RemoveEventListener(aType, aListener, aUseCapture);
9541 return NS_OK;
9544 NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsGlobalWindow)
9546 NS_IMETHODIMP
9547 nsGlobalWindow::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal)
9549 FORWARD_TO_INNER(DispatchEvent, (aEvent, aRetVal), NS_OK);
9551 if (!IsCurrentInnerWindow()) {
9552 NS_WARNING("DispatchEvent called on non-current inner window, dropping. "
9553 "Please check the window in the caller instead.");
9554 return NS_ERROR_FAILURE;
9557 if (!mDoc) {
9558 return NS_ERROR_FAILURE;
9561 // Obtain a presentation shell
9562 nsIPresShell *shell = mDoc->GetShell();
9563 nsRefPtr<nsPresContext> presContext;
9564 if (shell) {
9565 // Retrieve the context
9566 presContext = shell->GetPresContext();
9569 nsEventStatus status = nsEventStatus_eIgnore;
9570 nsresult rv =
9571 EventDispatcher::DispatchDOMEvent(GetOuterWindow(), nullptr, aEvent,
9572 presContext, &status);
9574 *aRetVal = (status != nsEventStatus_eConsumeNoDefault);
9575 return rv;
9578 NS_IMETHODIMP
9579 nsGlobalWindow::AddEventListener(const nsAString& aType,
9580 nsIDOMEventListener *aListener,
9581 bool aUseCapture, bool aWantsUntrusted,
9582 uint8_t aOptionalArgc)
9584 NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
9585 "Won't check if this is chrome, you want to set "
9586 "aWantsUntrusted to false or make the aWantsUntrusted "
9587 "explicit by making optional_argc non-zero.");
9589 if (IsOuterWindow() && mInnerWindow &&
9590 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9591 return NS_ERROR_DOM_SECURITY_ERR;
9594 if (!aWantsUntrusted &&
9595 (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
9596 aWantsUntrusted = true;
9599 EventListenerManager* manager = GetOrCreateListenerManager();
9600 NS_ENSURE_STATE(manager);
9601 manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted);
9602 return NS_OK;
9605 void
9606 nsGlobalWindow::AddEventListener(const nsAString& aType,
9607 EventListener* aListener,
9608 bool aUseCapture,
9609 const Nullable<bool>& aWantsUntrusted,
9610 ErrorResult& aRv)
9612 if (IsOuterWindow() && mInnerWindow &&
9613 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9614 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
9615 return;
9618 bool wantsUntrusted;
9619 if (aWantsUntrusted.IsNull()) {
9620 wantsUntrusted = !nsContentUtils::IsChromeDoc(mDoc);
9621 } else {
9622 wantsUntrusted = aWantsUntrusted.Value();
9625 EventListenerManager* manager = GetOrCreateListenerManager();
9626 if (!manager) {
9627 aRv.Throw(NS_ERROR_UNEXPECTED);
9628 return;
9630 manager->AddEventListener(aType, aListener, aUseCapture, wantsUntrusted);
9633 NS_IMETHODIMP
9634 nsGlobalWindow::AddSystemEventListener(const nsAString& aType,
9635 nsIDOMEventListener *aListener,
9636 bool aUseCapture,
9637 bool aWantsUntrusted,
9638 uint8_t aOptionalArgc)
9640 NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
9641 "Won't check if this is chrome, you want to set "
9642 "aWantsUntrusted to false or make the aWantsUntrusted "
9643 "explicit by making optional_argc non-zero.");
9645 if (IsOuterWindow() && mInnerWindow &&
9646 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9647 return NS_ERROR_DOM_SECURITY_ERR;
9650 if (!aWantsUntrusted &&
9651 (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
9652 aWantsUntrusted = true;
9655 return NS_AddSystemEventListener(this, aType, aListener, aUseCapture,
9656 aWantsUntrusted);
9659 EventListenerManager*
9660 nsGlobalWindow::GetOrCreateListenerManager()
9662 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
9664 if (!mListenerManager) {
9665 mListenerManager =
9666 new EventListenerManager(static_cast<EventTarget*>(this));
9669 return mListenerManager;
9672 EventListenerManager*
9673 nsGlobalWindow::GetExistingListenerManager() const
9675 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
9677 return mListenerManager;
9680 nsIScriptContext*
9681 nsGlobalWindow::GetContextForEventHandlers(nsresult* aRv)
9683 *aRv = NS_ERROR_UNEXPECTED;
9684 NS_ENSURE_TRUE(!IsInnerWindow() || IsCurrentInnerWindow(), nullptr);
9686 nsIScriptContext* scx;
9687 if ((scx = GetContext())) {
9688 *aRv = NS_OK;
9689 return scx;
9691 return nullptr;
9694 //*****************************************************************************
9695 // nsGlobalWindow::nsPIDOMWindow
9696 //*****************************************************************************
9698 nsPIDOMWindow*
9699 nsGlobalWindow::GetPrivateParent()
9701 MOZ_ASSERT(IsOuterWindow());
9703 nsCOMPtr<nsIDOMWindow> parent;
9704 GetParent(getter_AddRefs(parent));
9706 if (static_cast<nsIDOMWindow *>(this) == parent.get()) {
9707 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
9708 if (!chromeElement)
9709 return nullptr; // This is ok, just means a null parent.
9711 nsIDocument* doc = chromeElement->GetComposedDoc();
9712 if (!doc)
9713 return nullptr; // This is ok, just means a null parent.
9715 return doc->GetWindow();
9718 if (parent) {
9719 return static_cast<nsGlobalWindow *>
9720 (static_cast<nsIDOMWindow*>(parent.get()));
9723 return nullptr;
9726 nsPIDOMWindow*
9727 nsGlobalWindow::GetPrivateRoot()
9729 if (IsInnerWindow()) {
9730 nsGlobalWindow* outer = GetOuterWindowInternal();
9731 if (!outer) {
9732 NS_WARNING("No outer window available!");
9733 return nullptr;
9735 return outer->GetPrivateRoot();
9738 nsCOMPtr<nsIDOMWindow> top;
9739 GetTop(getter_AddRefs(top));
9741 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
9742 if (chromeElement) {
9743 nsIDocument* doc = chromeElement->GetComposedDoc();
9744 if (doc) {
9745 nsIDOMWindow *parent = doc->GetWindow();
9746 if (parent) {
9747 parent->GetTop(getter_AddRefs(top));
9752 return static_cast<nsGlobalWindow*>(top.get());
9756 nsLocation*
9757 nsGlobalWindow::GetLocation(ErrorResult& aError)
9759 FORWARD_TO_INNER_OR_THROW(GetLocation, (aError), aError, nullptr);
9761 nsIDocShell *docShell = GetDocShell();
9762 if (!mLocation && docShell) {
9763 mLocation = new nsLocation(this, docShell);
9765 return mLocation;
9768 NS_IMETHODIMP
9769 nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation)
9771 ErrorResult rv;
9772 nsCOMPtr<nsIDOMLocation> location = GetLocation(rv);
9773 location.forget(aLocation);
9775 return rv.ErrorCode();
9778 void
9779 nsGlobalWindow::ActivateOrDeactivate(bool aActivate)
9781 MOZ_ASSERT(IsOuterWindow());
9783 // Set / unset mIsActive on the top level window, which is used for the
9784 // :-moz-window-inactive pseudoclass, and its sheet (if any).
9785 nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
9786 if (!mainWidget)
9787 return;
9789 // Get the top level widget (if the main widget is a sheet, this will
9790 // be the sheet's top (non-sheet) parent).
9791 nsCOMPtr<nsIWidget> topLevelWidget = mainWidget->GetSheetWindowParent();
9792 if (!topLevelWidget) {
9793 topLevelWidget = mainWidget;
9796 nsCOMPtr<nsPIDOMWindow> piMainWindow(
9797 do_QueryInterface(static_cast<nsIDOMWindow*>(this)));
9798 piMainWindow->SetActive(aActivate);
9800 if (mainWidget != topLevelWidget) {
9801 // This is a workaround for the following problem:
9802 // When a window with an open sheet gains or loses focus, only the sheet
9803 // window receives the NS_ACTIVATE/NS_DEACTIVATE event. However the
9804 // styling of the containing top level window also needs to change. We
9805 // get around this by calling nsPIDOMWindow::SetActive() on both windows.
9807 // Get the top level widget's nsGlobalWindow
9808 nsCOMPtr<nsIDOMWindow> topLevelWindow;
9810 // widgetListener should be a nsXULWindow
9811 nsIWidgetListener* listener = topLevelWidget->GetWidgetListener();
9812 if (listener) {
9813 nsCOMPtr<nsIXULWindow> window = listener->GetXULWindow();
9814 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(window));
9815 topLevelWindow = do_GetInterface(req);
9818 if (topLevelWindow) {
9819 nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(topLevelWindow));
9820 piWin->SetActive(aActivate);
9825 static bool
9826 NotifyDocumentTree(nsIDocument* aDocument, void* aData)
9828 aDocument->EnumerateSubDocuments(NotifyDocumentTree, nullptr);
9829 aDocument->DocumentStatesChanged(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
9830 return true;
9833 void
9834 nsGlobalWindow::SetActive(bool aActive)
9836 nsPIDOMWindow::SetActive(aActive);
9837 NotifyDocumentTree(mDoc, nullptr);
9840 void nsGlobalWindow::SetIsBackground(bool aIsBackground)
9842 MOZ_ASSERT(IsOuterWindow());
9844 bool resetTimers = (!aIsBackground && IsBackground());
9845 nsPIDOMWindow::SetIsBackground(aIsBackground);
9846 if (resetTimers) {
9847 ResetTimersForNonBackgroundWindow();
9849 #ifdef MOZ_GAMEPAD
9850 if (!aIsBackground) {
9851 nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
9852 if (inner) {
9853 inner->SyncGamepadState();
9856 #endif
9859 void nsGlobalWindow::MaybeUpdateTouchState()
9861 FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
9863 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9865 nsCOMPtr<nsIDOMWindow> focusedWindow;
9866 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
9868 if(this == focusedWindow) {
9869 UpdateTouchState();
9872 if (mMayHaveTouchEventListener) {
9873 nsCOMPtr<nsIObserverService> observerService =
9874 services::GetObserverService();
9876 if (observerService) {
9877 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
9878 DOM_TOUCH_LISTENER_ADDED,
9879 nullptr);
9884 void nsGlobalWindow::UpdateTouchState()
9886 FORWARD_TO_INNER_VOID(UpdateTouchState, ());
9888 nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
9889 if (!mainWidget) {
9890 return;
9893 if (mMayHaveTouchEventListener) {
9894 mainWidget->RegisterTouchWindow();
9895 } else {
9896 mainWidget->UnregisterTouchWindow();
9900 void
9901 nsGlobalWindow::EnableGamepadUpdates()
9903 MOZ_ASSERT(IsInnerWindow());
9905 if (mHasGamepad) {
9906 #ifdef MOZ_GAMEPAD
9907 nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
9908 if (gamepadsvc) {
9909 gamepadsvc->AddListener(this);
9911 #endif
9915 void
9916 nsGlobalWindow::DisableGamepadUpdates()
9918 MOZ_ASSERT(IsInnerWindow());
9920 if (mHasGamepad) {
9921 #ifdef MOZ_GAMEPAD
9922 nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
9923 if (gamepadsvc) {
9924 gamepadsvc->RemoveListener(this);
9926 #endif
9930 void
9931 nsGlobalWindow::SetChromeEventHandler(EventTarget* aChromeEventHandler)
9933 MOZ_ASSERT(IsOuterWindow());
9935 SetChromeEventHandlerInternal(aChromeEventHandler);
9936 // update the chrome event handler on all our inner windows
9937 for (nsGlobalWindow *inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
9938 inner != this;
9939 inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
9940 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
9941 "bad outer window pointer");
9942 inner->SetChromeEventHandlerInternal(aChromeEventHandler);
9946 static bool IsLink(nsIContent* aContent)
9948 return aContent && (aContent->IsHTML(nsGkAtoms::a) ||
9949 aContent->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
9950 nsGkAtoms::simple, eCaseMatters));
9953 void
9954 nsGlobalWindow::SetFocusedNode(nsIContent* aNode,
9955 uint32_t aFocusMethod,
9956 bool aNeedsFocus)
9958 FORWARD_TO_INNER_VOID(SetFocusedNode, (aNode, aFocusMethod, aNeedsFocus));
9960 if (aNode && aNode->GetComposedDoc() != mDoc) {
9961 NS_WARNING("Trying to set focus to a node from a wrong document");
9962 return;
9965 if (mCleanedUp) {
9966 NS_ASSERTION(!aNode, "Trying to focus cleaned up window!");
9967 aNode = nullptr;
9968 aNeedsFocus = false;
9970 if (mFocusedNode != aNode) {
9971 UpdateCanvasFocus(false, aNode);
9972 mFocusedNode = aNode;
9973 mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
9974 mShowFocusRingForContent = false;
9977 if (mFocusedNode) {
9978 // if a node was focused by a keypress, turn on focus rings for the
9979 // window.
9980 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
9981 mFocusByKeyOccurred = true;
9982 } else if (
9983 // otherwise, we set mShowFocusRingForContent, as we don't want this to
9984 // be permanent for the window. On Windows, focus rings are only shown
9985 // when the FLAG_SHOWRING flag is used. On other platforms, focus rings
9986 // are only hidden for clicks on links.
9987 #ifndef XP_WIN
9988 !(mFocusMethod & nsIFocusManager::FLAG_BYMOUSE) || !IsLink(aNode) ||
9989 #endif
9990 aFocusMethod & nsIFocusManager::FLAG_SHOWRING) {
9991 mShowFocusRingForContent = true;
9995 if (aNeedsFocus)
9996 mNeedsFocus = aNeedsFocus;
9999 uint32_t
10000 nsGlobalWindow::GetFocusMethod()
10002 FORWARD_TO_INNER(GetFocusMethod, (), 0);
10004 return mFocusMethod;
10007 bool
10008 nsGlobalWindow::ShouldShowFocusRing()
10010 FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
10012 return mShowFocusRings || mShowFocusRingForContent || mFocusByKeyOccurred;
10015 void
10016 nsGlobalWindow::SetKeyboardIndicators(UIStateChangeType aShowAccelerators,
10017 UIStateChangeType aShowFocusRings)
10019 FORWARD_TO_INNER_VOID(SetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
10021 bool oldShouldShowFocusRing = ShouldShowFocusRing();
10023 // only change the flags that have been modified
10024 if (aShowAccelerators != UIStateChangeType_NoChange)
10025 mShowAccelerators = aShowAccelerators == UIStateChangeType_Set;
10026 if (aShowFocusRings != UIStateChangeType_NoChange)
10027 mShowFocusRings = aShowFocusRings == UIStateChangeType_Set;
10029 // propagate the indicators to child windows
10030 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
10031 if (docShell) {
10032 int32_t childCount = 0;
10033 docShell->GetChildCount(&childCount);
10035 for (int32_t i = 0; i < childCount; ++i) {
10036 nsCOMPtr<nsIDocShellTreeItem> childShell;
10037 docShell->GetChildAt(i, getter_AddRefs(childShell));
10038 if (!childShell) {
10039 continue;
10042 nsCOMPtr<nsPIDOMWindow> childWindow = childShell->GetWindow();
10043 if (childWindow) {
10044 childWindow->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
10049 bool newShouldShowFocusRing = ShouldShowFocusRing();
10050 if (mHasFocus && mFocusedNode &&
10051 oldShouldShowFocusRing != newShouldShowFocusRing &&
10052 mFocusedNode->IsElement()) {
10053 // Update mFocusedNode's state.
10054 if (newShouldShowFocusRing) {
10055 mFocusedNode->AsElement()->AddStates(NS_EVENT_STATE_FOCUSRING);
10056 } else {
10057 mFocusedNode->AsElement()->RemoveStates(NS_EVENT_STATE_FOCUSRING);
10062 void
10063 nsGlobalWindow::GetKeyboardIndicators(bool* aShowAccelerators,
10064 bool* aShowFocusRings)
10066 FORWARD_TO_INNER_VOID(GetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
10068 *aShowAccelerators = mShowAccelerators;
10069 *aShowFocusRings = mShowFocusRings;
10072 bool
10073 nsGlobalWindow::TakeFocus(bool aFocus, uint32_t aFocusMethod)
10075 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
10077 if (mCleanedUp) {
10078 return false;
10081 if (aFocus)
10082 mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
10084 if (mHasFocus != aFocus) {
10085 mHasFocus = aFocus;
10086 UpdateCanvasFocus(true, mFocusedNode);
10089 // if mNeedsFocus is true, then the document has not yet received a
10090 // document-level focus event. If there is a root content node, then return
10091 // true to tell the calling focus manager that a focus event is expected. If
10092 // there is no root content node, the document hasn't loaded enough yet, or
10093 // there isn't one and there is no point in firing a focus event.
10094 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
10095 mNeedsFocus = false;
10096 return true;
10099 mNeedsFocus = false;
10100 return false;
10103 void
10104 nsGlobalWindow::SetReadyForFocus()
10106 FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
10108 bool oldNeedsFocus = mNeedsFocus;
10109 mNeedsFocus = false;
10111 // update whether focus rings need to be shown using the state from the
10112 // root window
10113 nsPIDOMWindow* root = GetPrivateRoot();
10114 if (root) {
10115 bool showAccelerators, showFocusRings;
10116 root->GetKeyboardIndicators(&showAccelerators, &showFocusRings);
10117 mShowFocusRings = showFocusRings;
10120 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
10121 if (fm)
10122 fm->WindowShown(this, oldNeedsFocus);
10125 void
10126 nsGlobalWindow::PageHidden()
10128 FORWARD_TO_INNER_VOID(PageHidden, ());
10130 // the window is being hidden, so tell the focus manager that the frame is
10131 // no longer valid. Use the persisted field to determine if the document
10132 // is being destroyed.
10134 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
10135 if (fm)
10136 fm->WindowHidden(this);
10138 mNeedsFocus = true;
10141 class HashchangeCallback : public nsRunnable
10143 public:
10144 HashchangeCallback(const nsAString &aOldURL,
10145 const nsAString &aNewURL,
10146 nsGlobalWindow* aWindow)
10147 : mWindow(aWindow)
10149 MOZ_ASSERT(mWindow);
10150 MOZ_ASSERT(mWindow->IsInnerWindow());
10151 mOldURL.Assign(aOldURL);
10152 mNewURL.Assign(aNewURL);
10155 NS_IMETHOD Run()
10157 NS_PRECONDITION(NS_IsMainThread(), "Should be called on the main thread.");
10158 return mWindow->FireHashchange(mOldURL, mNewURL);
10161 private:
10162 nsString mOldURL;
10163 nsString mNewURL;
10164 nsRefPtr<nsGlobalWindow> mWindow;
10167 nsresult
10168 nsGlobalWindow::DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI)
10170 FORWARD_TO_INNER(DispatchAsyncHashchange, (aOldURI, aNewURI), NS_OK);
10172 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
10173 // their hashes are different.
10174 nsAutoCString oldBeforeHash, oldHash, newBeforeHash, newHash;
10175 nsContentUtils::SplitURIAtHash(aOldURI, oldBeforeHash, oldHash);
10176 nsContentUtils::SplitURIAtHash(aNewURI, newBeforeHash, newHash);
10178 NS_ENSURE_STATE(oldBeforeHash.Equals(newBeforeHash));
10179 NS_ENSURE_STATE(!oldHash.Equals(newHash));
10181 nsAutoCString oldSpec, newSpec;
10182 aOldURI->GetSpec(oldSpec);
10183 aNewURI->GetSpec(newSpec);
10185 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
10186 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
10188 nsCOMPtr<nsIRunnable> callback =
10189 new HashchangeCallback(oldWideSpec, newWideSpec, this);
10190 return NS_DispatchToMainThread(callback);
10193 nsresult
10194 nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
10195 const nsAString &aNewURL)
10197 MOZ_ASSERT(IsInnerWindow());
10199 // Don't do anything if the window is frozen.
10200 if (IsFrozen())
10201 return NS_OK;
10203 // Get a presentation shell for use in creating the hashchange event.
10204 NS_ENSURE_STATE(IsCurrentInnerWindow());
10206 nsIPresShell *shell = mDoc->GetShell();
10207 nsRefPtr<nsPresContext> presContext;
10208 if (shell) {
10209 presContext = shell->GetPresContext();
10212 HashChangeEventInit init;
10213 init.mBubbles = true;
10214 init.mCancelable = false;
10215 init.mNewURL = aNewURL;
10216 init.mOldURL = aOldURL;
10218 nsRefPtr<HashChangeEvent> event =
10219 HashChangeEvent::Constructor(this, NS_LITERAL_STRING("hashchange"),
10220 init);
10222 event->SetTrusted(true);
10224 bool dummy;
10225 return DispatchEvent(event, &dummy);
10228 nsresult
10229 nsGlobalWindow::DispatchSyncPopState()
10231 FORWARD_TO_INNER(DispatchSyncPopState, (), NS_OK);
10233 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
10234 "Must be safe to run script here.");
10236 // Check that PopState hasn't been pref'ed off.
10237 if (!Preferences::GetBool(sPopStatePrefStr, false)) {
10238 return NS_OK;
10241 nsresult rv = NS_OK;
10243 // Bail if the window is frozen.
10244 if (IsFrozen()) {
10245 return NS_OK;
10248 // Get the document's pending state object -- it contains the data we're
10249 // going to send along with the popstate event. The object is serialized
10250 // using structured clone.
10251 nsCOMPtr<nsIVariant> stateObj;
10252 rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
10253 NS_ENSURE_SUCCESS(rv, rv);
10255 // Obtain a presentation shell for use in creating a popstate event.
10256 nsIPresShell *shell = mDoc->GetShell();
10257 nsRefPtr<nsPresContext> presContext;
10258 if (shell) {
10259 presContext = shell->GetPresContext();
10262 bool result = true;
10263 nsPIDOMWindow* outerWindow = GetOuterWindow();
10264 nsCOMPtr<EventTarget> outerWindowET = do_QueryInterface(outerWindow);
10265 NS_ENSURE_TRUE(outerWindowET, NS_ERROR_FAILURE);
10267 AutoJSAPI jsapi;
10268 result = jsapi.Init(outerWindow);
10269 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
10271 JSContext* cx = jsapi.cx();
10272 JS::Rooted<JS::Value> stateJSValue(cx, JS::NullValue());
10273 result = stateObj ? VariantToJsval(cx, stateObj, &stateJSValue) : true;
10274 NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
10276 RootedDictionary<PopStateEventInit> init(cx);
10277 init.mBubbles = true;
10278 init.mCancelable = false;
10279 init.mState = stateJSValue;
10281 nsRefPtr<PopStateEvent> event =
10282 PopStateEvent::Constructor(outerWindowET, NS_LITERAL_STRING("popstate"),
10283 init);
10284 event->SetTrusted(true);
10285 event->SetTarget(outerWindowET);
10287 bool dummy; // default action
10288 return DispatchEvent(event, &dummy);
10291 // Find an nsICanvasFrame under aFrame. Only search the principal
10292 // child lists. aFrame must be non-null.
10293 static nsCanvasFrame* FindCanvasFrame(nsIFrame* aFrame)
10295 nsCanvasFrame* canvasFrame = do_QueryFrame(aFrame);
10296 if (canvasFrame) {
10297 return canvasFrame;
10300 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
10301 while (kid) {
10302 canvasFrame = FindCanvasFrame(kid);
10303 if (canvasFrame) {
10304 return canvasFrame;
10306 kid = kid->GetNextSibling();
10309 return nullptr;
10312 //-------------------------------------------------------
10313 // Tells the HTMLFrame/CanvasFrame that is now has focus
10314 void
10315 nsGlobalWindow::UpdateCanvasFocus(bool aFocusChanged, nsIContent* aNewContent)
10317 MOZ_ASSERT(IsInnerWindow());
10319 // this is called from the inner window so use GetDocShell
10320 nsIDocShell* docShell = GetDocShell();
10321 if (!docShell)
10322 return;
10324 bool editable;
10325 docShell->GetEditable(&editable);
10326 if (editable)
10327 return;
10329 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
10330 if (!presShell || !mDoc)
10331 return;
10333 Element *rootElement = mDoc->GetRootElement();
10334 if (rootElement) {
10335 if ((mHasFocus || aFocusChanged) &&
10336 (mFocusedNode == rootElement || aNewContent == rootElement)) {
10337 nsIFrame* frame = rootElement->GetPrimaryFrame();
10338 if (frame) {
10339 frame = frame->GetParent();
10340 nsCanvasFrame* canvasFrame = do_QueryFrame(frame);
10341 if (canvasFrame) {
10342 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
10346 } else {
10347 // Look for the frame the hard way
10348 nsIFrame* frame = presShell->GetRootFrame();
10349 if (frame) {
10350 nsCanvasFrame* canvasFrame = FindCanvasFrame(frame);
10351 if (canvasFrame) {
10352 canvasFrame->SetHasFocus(false);
10358 already_AddRefed<nsICSSDeclaration>
10359 nsGlobalWindow::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt,
10360 ErrorResult& aError)
10362 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
10365 NS_IMETHODIMP
10366 nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt,
10367 const nsAString& aPseudoElt,
10368 nsIDOMCSSStyleDeclaration** aReturn)
10370 return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn);
10373 already_AddRefed<nsICSSDeclaration>
10374 nsGlobalWindow::GetDefaultComputedStyle(Element& aElt,
10375 const nsAString& aPseudoElt,
10376 ErrorResult& aError)
10378 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
10381 NS_IMETHODIMP
10382 nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt,
10383 const nsAString& aPseudoElt,
10384 nsIDOMCSSStyleDeclaration** aReturn)
10386 return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn);
10389 nsresult
10390 nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt,
10391 const nsAString& aPseudoElt,
10392 bool aDefaultStylesOnly,
10393 nsIDOMCSSStyleDeclaration** aReturn)
10395 NS_ENSURE_ARG_POINTER(aReturn);
10396 *aReturn = nullptr;
10398 nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
10399 if (!element) {
10400 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
10403 ErrorResult rv;
10404 nsCOMPtr<nsIDOMCSSStyleDeclaration> declaration =
10405 GetComputedStyleHelper(*element, aPseudoElt, aDefaultStylesOnly, rv);
10406 declaration.forget(aReturn);
10408 return rv.ErrorCode();
10411 already_AddRefed<nsICSSDeclaration>
10412 nsGlobalWindow::GetComputedStyleHelper(Element& aElt,
10413 const nsAString& aPseudoElt,
10414 bool aDefaultStylesOnly,
10415 ErrorResult& aError)
10417 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelper,
10418 (aElt, aPseudoElt, aDefaultStylesOnly, aError),
10419 aError, nullptr);
10421 if (!mDocShell) {
10422 return nullptr;
10425 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
10427 if (!presShell) {
10428 // Try flushing frames on our parent in case there's a pending
10429 // style change that will create the presshell.
10430 nsGlobalWindow *parent =
10431 static_cast<nsGlobalWindow *>(GetPrivateParent());
10432 if (!parent) {
10433 return nullptr;
10436 parent->FlushPendingNotifications(Flush_Frames);
10438 // Might have killed mDocShell
10439 if (!mDocShell) {
10440 return nullptr;
10443 presShell = mDocShell->GetPresShell();
10444 if (!presShell) {
10445 return nullptr;
10449 nsRefPtr<nsComputedDOMStyle> compStyle =
10450 NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell,
10451 aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
10452 nsComputedDOMStyle::eAll);
10454 return compStyle.forget();
10457 DOMStorage*
10458 nsGlobalWindow::GetSessionStorage(ErrorResult& aError)
10460 FORWARD_TO_INNER_OR_THROW(GetSessionStorage, (aError), aError, nullptr);
10462 nsIPrincipal *principal = GetPrincipal();
10463 nsIDocShell* docShell = GetDocShell();
10465 if (!principal || !docShell || !Preferences::GetBool(kStorageEnabled)) {
10466 return nullptr;
10469 if (mSessionStorage) {
10470 #ifdef PR_LOGGING
10471 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
10472 PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get());
10474 #endif
10475 bool canAccess = mSessionStorage->CanAccess(principal);
10476 NS_ASSERTION(canAccess,
10477 "This window owned sessionStorage "
10478 "that could not be accessed!");
10479 if (!canAccess) {
10480 mSessionStorage = nullptr;
10484 if (!mSessionStorage) {
10485 nsString documentURI;
10486 if (mDoc) {
10487 mDoc->GetDocumentURI(documentURI);
10490 // If the document has the sandboxed origin flag set
10491 // don't allow access to sessionStorage.
10492 if (!mDoc) {
10493 aError.Throw(NS_ERROR_FAILURE);
10494 return nullptr;
10497 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
10498 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10499 return nullptr;
10502 nsresult rv;
10504 nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
10505 if (NS_FAILED(rv)) {
10506 aError.Throw(rv);
10507 return nullptr;
10510 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
10512 nsCOMPtr<nsIDOMStorage> storage;
10513 aError = storageManager->CreateStorage(this, principal, documentURI,
10514 loadContext && loadContext->UsePrivateBrowsing(),
10515 getter_AddRefs(storage));
10516 if (aError.Failed()) {
10517 return nullptr;
10520 mSessionStorage = static_cast<DOMStorage*>(storage.get());
10521 MOZ_ASSERT(mSessionStorage);
10523 #ifdef PR_LOGGING
10524 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
10525 PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get());
10527 #endif
10529 if (!mSessionStorage) {
10530 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10531 return nullptr;
10535 #ifdef PR_LOGGING
10536 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
10537 PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
10539 #endif
10541 return mSessionStorage;
10544 NS_IMETHODIMP
10545 nsGlobalWindow::GetSessionStorage(nsISupports** aSessionStorage)
10547 ErrorResult rv;
10548 nsCOMPtr<nsIDOMStorage> storage = GetSessionStorage(rv);
10549 storage.forget(aSessionStorage);
10551 return rv.ErrorCode();
10554 DOMStorage*
10555 nsGlobalWindow::GetLocalStorage(ErrorResult& aError)
10557 FORWARD_TO_INNER_OR_THROW(GetLocalStorage, (aError), aError, nullptr);
10559 if (!Preferences::GetBool(kStorageEnabled)) {
10560 return nullptr;
10563 if (!mLocalStorage) {
10564 if (!DOMStorage::CanUseStorage()) {
10565 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10566 return nullptr;
10569 nsIPrincipal *principal = GetPrincipal();
10570 if (!principal) {
10571 return nullptr;
10574 nsresult rv;
10575 nsCOMPtr<nsIDOMStorageManager> storageManager =
10576 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
10577 if (NS_FAILED(rv)) {
10578 aError.Throw(rv);
10579 return nullptr;
10582 // If the document has the sandboxed origin flag set
10583 // don't allow access to localStorage.
10584 if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
10585 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10586 return nullptr;
10589 nsString documentURI;
10590 if (mDoc) {
10591 mDoc->GetDocumentURI(documentURI);
10594 nsIDocShell* docShell = GetDocShell();
10595 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
10597 nsCOMPtr<nsIDOMStorage> storage;
10598 aError = storageManager->CreateStorage(this, principal, documentURI,
10599 loadContext && loadContext->UsePrivateBrowsing(),
10600 getter_AddRefs(storage));
10601 if (aError.Failed()) {
10602 return nullptr;
10605 mLocalStorage = static_cast<DOMStorage*>(storage.get());
10606 MOZ_ASSERT(mLocalStorage);
10609 return mLocalStorage;
10612 NS_IMETHODIMP
10613 nsGlobalWindow::GetLocalStorage(nsISupports** aLocalStorage)
10615 NS_ENSURE_ARG(aLocalStorage);
10617 ErrorResult rv;
10618 nsCOMPtr<nsIDOMStorage> storage = GetLocalStorage(rv);
10619 storage.forget(aLocalStorage);
10621 return rv.ErrorCode();
10624 static bool
10625 GetIndexedDBEnabledForAboutURI(nsIURI *aURI)
10627 nsCOMPtr<nsIAboutModule> module;
10628 nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(module));
10629 NS_ENSURE_SUCCESS(rv, false);
10631 uint32_t flags;
10632 rv = module->GetURIFlags(aURI, &flags);
10633 NS_ENSURE_SUCCESS(rv, false);
10635 return flags & nsIAboutModule::ENABLE_INDEXED_DB;
10638 indexedDB::IDBFactory*
10639 nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
10641 if (!mIndexedDB) {
10642 // If the document has the sandboxed origin flag set
10643 // don't allow access to indexedDB.
10644 if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
10645 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10646 return nullptr;
10649 if (!IsChromeWindow()) {
10650 // Whitelist about:home, since it doesn't have a base domain it would not
10651 // pass the thirdPartyUtil check, though it should be able to use
10652 // indexedDB.
10653 bool skipThirdPartyCheck = false;
10654 nsIPrincipal *principal = GetPrincipal();
10655 if (principal) {
10656 nsCOMPtr<nsIURI> uri;
10657 principal->GetURI(getter_AddRefs(uri));
10659 if (uri) {
10660 bool isAbout = false;
10661 if (NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
10662 skipThirdPartyCheck = GetIndexedDBEnabledForAboutURI(uri);
10667 if (!skipThirdPartyCheck) {
10668 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
10669 do_GetService(THIRDPARTYUTIL_CONTRACTID);
10670 if (!thirdPartyUtil) {
10671 aError.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
10672 return nullptr;
10675 bool isThirdParty;
10676 aError = thirdPartyUtil->IsThirdPartyWindow(this, nullptr,
10677 &isThirdParty);
10678 if (aError.Failed() || isThirdParty) {
10679 NS_WARN_IF_FALSE(aError.Failed(),
10680 "IndexedDB is not permitted in a third-party window.");
10681 return nullptr;
10686 // This may be null if being created from a file.
10687 aError = indexedDB::IDBFactory::Create(this, nullptr,
10688 getter_AddRefs(mIndexedDB));
10691 return mIndexedDB;
10694 NS_IMETHODIMP
10695 nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
10697 ErrorResult rv;
10698 nsCOMPtr<nsISupports> request(GetIndexedDB(rv));
10699 request.forget(_retval);
10701 return rv.ErrorCode();
10704 NS_IMETHODIMP
10705 nsGlobalWindow::GetMozIndexedDB(nsISupports** _retval)
10707 return GetIndexedDB(_retval);
10710 //*****************************************************************************
10711 // nsGlobalWindow::nsIInterfaceRequestor
10712 //*****************************************************************************
10714 NS_IMETHODIMP
10715 nsGlobalWindow::GetInterface(const nsIID & aIID, void **aSink)
10717 NS_ENSURE_ARG_POINTER(aSink);
10718 *aSink = nullptr;
10720 if (aIID.Equals(NS_GET_IID(nsIDocCharset))) {
10721 nsGlobalWindow* outer = GetOuterWindowInternal();
10722 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10724 NS_WARNING("Using deprecated nsIDocCharset: use nsIDocShell.GetCharset() instead ");
10725 nsCOMPtr<nsIDocCharset> docCharset(do_QueryInterface(outer->mDocShell));
10726 docCharset.forget(aSink);
10728 else if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) {
10729 nsGlobalWindow* outer = GetOuterWindowInternal();
10730 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10732 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(outer->mDocShell));
10733 webNav.forget(aSink);
10735 else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
10736 nsGlobalWindow* outer = GetOuterWindowInternal();
10737 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10739 nsCOMPtr<nsIDocShell> docShell = outer->mDocShell;
10740 docShell.forget(aSink);
10742 #ifdef NS_PRINTING
10743 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
10744 nsGlobalWindow* outer = GetOuterWindowInternal();
10745 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10747 if (outer->mDocShell) {
10748 nsCOMPtr<nsIContentViewer> viewer;
10749 outer->mDocShell->GetContentViewer(getter_AddRefs(viewer));
10750 if (viewer) {
10751 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer));
10752 webBrowserPrint.forget(aSink);
10756 #endif
10757 else if (aIID.Equals(NS_GET_IID(nsIDOMWindowUtils))) {
10758 nsGlobalWindow* outer = GetOuterWindowInternal();
10759 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10761 if (!mWindowUtils) {
10762 mWindowUtils = new nsDOMWindowUtils(outer);
10765 *aSink = mWindowUtils;
10766 NS_ADDREF(((nsISupports *) *aSink));
10768 else if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
10769 nsGlobalWindow* outer = GetOuterWindowInternal();
10770 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10772 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(outer->mDocShell));
10773 loadContext.forget(aSink);
10775 else {
10776 return QueryInterface(aIID, aSink);
10779 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
10782 void
10783 nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID,
10784 JS::MutableHandle<JS::Value> aRetval,
10785 ErrorResult& aError)
10787 dom::GetInterface(aCx, this, aIID, aRetval, aError);
10790 void
10791 nsGlobalWindow::FireOfflineStatusEvent()
10793 if (!IsCurrentInnerWindow())
10794 return;
10795 nsAutoString name;
10796 if (NS_IsOffline()) {
10797 name.AssignLiteral("offline");
10798 } else {
10799 name.AssignLiteral("online");
10801 // The event is fired at the body element, or if there is no body element,
10802 // at the document.
10803 nsCOMPtr<EventTarget> eventTarget = mDoc.get();
10804 nsHTMLDocument* htmlDoc = mDoc->AsHTMLDocument();
10805 if (htmlDoc) {
10806 Element* body = htmlDoc->GetBody();
10807 if (body) {
10808 eventTarget = body;
10810 } else {
10811 Element* documentElement = mDoc->GetDocumentElement();
10812 if (documentElement) {
10813 eventTarget = documentElement;
10816 nsContentUtils::DispatchTrustedEvent(mDoc, eventTarget, name, true, false);
10819 class NotifyIdleObserverRunnable : public nsRunnable
10821 public:
10822 NotifyIdleObserverRunnable(nsIIdleObserver* aIdleObserver,
10823 uint32_t aTimeInS,
10824 bool aCallOnidle,
10825 nsGlobalWindow* aIdleWindow)
10826 : mIdleObserver(aIdleObserver), mTimeInS(aTimeInS), mIdleWindow(aIdleWindow),
10827 mCallOnidle(aCallOnidle)
10830 NS_IMETHOD Run()
10832 if (mIdleWindow->ContainsIdleObserver(mIdleObserver, mTimeInS)) {
10833 return mCallOnidle ? mIdleObserver->Onidle() : mIdleObserver->Onactive();
10835 return NS_OK;
10838 private:
10839 nsCOMPtr<nsIIdleObserver> mIdleObserver;
10840 uint32_t mTimeInS;
10841 nsRefPtr<nsGlobalWindow> mIdleWindow;
10843 // If false then call on active
10844 bool mCallOnidle;
10847 void
10848 nsGlobalWindow::NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
10849 bool aCallOnidle)
10851 MOZ_ASSERT(aIdleObserverHolder);
10852 aIdleObserverHolder->mPrevNotificationIdle = aCallOnidle;
10854 nsCOMPtr<nsIRunnable> caller =
10855 new NotifyIdleObserverRunnable(aIdleObserverHolder->mIdleObserver,
10856 aIdleObserverHolder->mTimeInS,
10857 aCallOnidle, this);
10858 if (NS_FAILED(NS_DispatchToCurrentThread(caller))) {
10859 NS_WARNING("Failed to dispatch thread for idle observer notification.");
10863 bool
10864 nsGlobalWindow::ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t aTimeInS)
10866 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
10867 bool found = false;
10868 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
10869 while (iter.HasMore()) {
10870 IdleObserverHolder& idleObserver = iter.GetNext();
10871 if (idleObserver.mIdleObserver == aIdleObserver &&
10872 idleObserver.mTimeInS == aTimeInS) {
10873 found = true;
10874 break;
10877 return found;
10880 void
10881 IdleActiveTimerCallback(nsITimer* aTimer, void* aClosure)
10883 nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure);
10884 MOZ_ASSERT(idleWindow, "Idle window has not been instantiated.");
10885 idleWindow->HandleIdleActiveEvent();
10888 void
10889 IdleObserverTimerCallback(nsITimer* aTimer, void* aClosure)
10891 nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure);
10892 MOZ_ASSERT(idleWindow, "Idle window has not been instantiated.");
10893 idleWindow->HandleIdleObserverCallback();
10896 void
10897 nsGlobalWindow::HandleIdleObserverCallback()
10899 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10900 MOZ_ASSERT(static_cast<uint32_t>(mIdleCallbackIndex) < mIdleObservers.Length(),
10901 "Idle callback index exceeds array bounds!");
10902 IdleObserverHolder& idleObserver = mIdleObservers.ElementAt(mIdleCallbackIndex);
10903 NotifyIdleObserver(&idleObserver, true);
10904 mIdleCallbackIndex++;
10905 if (NS_FAILED(ScheduleNextIdleObserverCallback())) {
10906 NS_WARNING("Failed to set next idle observer callback.");
10910 nsresult
10911 nsGlobalWindow::ScheduleNextIdleObserverCallback()
10913 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10914 MOZ_ASSERT(mIdleService, "No idle service!");
10916 if (mIdleCallbackIndex < 0 ||
10917 static_cast<uint32_t>(mIdleCallbackIndex) >= mIdleObservers.Length()) {
10918 return NS_OK;
10921 IdleObserverHolder& idleObserver =
10922 mIdleObservers.ElementAt(mIdleCallbackIndex);
10924 uint32_t userIdleTimeMS = 0;
10925 nsresult rv = mIdleService->GetIdleTime(&userIdleTimeMS);
10926 NS_ENSURE_SUCCESS(rv, rv);
10928 uint32_t callbackTimeMS = 0;
10929 if (idleObserver.mTimeInS * 1000 + mIdleFuzzFactor > userIdleTimeMS) {
10930 callbackTimeMS = idleObserver.mTimeInS * 1000 - userIdleTimeMS + mIdleFuzzFactor;
10933 mIdleTimer->Cancel();
10934 rv = mIdleTimer->InitWithFuncCallback(IdleObserverTimerCallback,
10935 this,
10936 callbackTimeMS,
10937 nsITimer::TYPE_ONE_SHOT);
10938 NS_ENSURE_SUCCESS(rv, rv);
10940 return NS_OK;
10943 uint32_t
10944 nsGlobalWindow::GetFuzzTimeMS()
10946 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10948 if (sIdleObserversAPIFuzzTimeDisabled) {
10949 return 0;
10952 uint32_t randNum = MAX_IDLE_FUZZ_TIME_MS;
10953 size_t nbytes = PR_GetRandomNoise(&randNum, sizeof(randNum));
10954 if (nbytes != sizeof(randNum)) {
10955 NS_WARNING("PR_GetRandomNoise(...) Not implemented or no available noise!");
10956 return MAX_IDLE_FUZZ_TIME_MS;
10959 if (randNum > MAX_IDLE_FUZZ_TIME_MS) {
10960 randNum %= MAX_IDLE_FUZZ_TIME_MS;
10963 return randNum;
10966 nsresult
10967 nsGlobalWindow::ScheduleActiveTimerCallback()
10969 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10971 if (!mAddActiveEventFuzzTime) {
10972 return HandleIdleActiveEvent();
10975 MOZ_ASSERT(mIdleTimer);
10976 mIdleTimer->Cancel();
10978 uint32_t fuzzFactorInMS = GetFuzzTimeMS();
10979 nsresult rv = mIdleTimer->InitWithFuncCallback(IdleActiveTimerCallback,
10980 this,
10981 fuzzFactorInMS,
10982 nsITimer::TYPE_ONE_SHOT);
10983 NS_ENSURE_SUCCESS(rv, rv);
10984 return NS_OK;
10987 nsresult
10988 nsGlobalWindow::HandleIdleActiveEvent()
10990 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10992 if (mCurrentlyIdle) {
10993 mIdleCallbackIndex = 0;
10994 mIdleFuzzFactor = GetFuzzTimeMS();
10995 nsresult rv = ScheduleNextIdleObserverCallback();
10996 NS_ENSURE_SUCCESS(rv, rv);
10997 return NS_OK;
11000 mIdleCallbackIndex = -1;
11001 MOZ_ASSERT(mIdleTimer);
11002 mIdleTimer->Cancel();
11003 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
11004 while (iter.HasMore()) {
11005 IdleObserverHolder& idleObserver = iter.GetNext();
11006 if (idleObserver.mPrevNotificationIdle) {
11007 NotifyIdleObserver(&idleObserver, false);
11011 return NS_OK;
11014 nsGlobalWindow::SlowScriptResponse
11015 nsGlobalWindow::ShowSlowScriptDialog()
11017 MOZ_ASSERT(IsInnerWindow());
11019 nsresult rv;
11020 AutoJSContext cx;
11022 // If it isn't safe to run script, then it isn't safe to bring up the prompt
11023 // (since that spins the event loop). In that (rare) case, we just kill the
11024 // script and report a warning.
11025 if (!nsContentUtils::IsSafeToRunScript()) {
11026 JS_ReportWarning(cx, "A long running script was terminated");
11027 return KillSlowScript;
11030 // If our document is not active, just kill the script: we've been unloaded
11031 if (!HasActiveDocument()) {
11032 return KillSlowScript;
11035 // Get the nsIPrompt interface from the docshell
11036 nsCOMPtr<nsIDocShell> ds = GetDocShell();
11037 NS_ENSURE_TRUE(ds, KillSlowScript);
11038 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
11039 NS_ENSURE_TRUE(prompt, KillSlowScript);
11041 // Check if we should offer the option to debug
11042 JS::AutoFilename filename;
11043 unsigned lineno;
11044 bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);
11046 // Prioritize the SlowScriptDebug interface over JSD1.
11047 nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
11049 if (hasFrame) {
11050 const char *debugCID = "@mozilla.org/dom/slow-script-debug;1";
11051 nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
11052 if (NS_SUCCEEDED(rv)) {
11053 debugService->GetActivationHandler(getter_AddRefs(debugCallback));
11057 bool showDebugButton = !!debugCallback;
11059 // Get localizable strings
11060 nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
11062 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11063 "KillScriptTitle",
11064 title);
11066 nsresult tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11067 "StopScriptButton",
11068 stopButton);
11069 if (NS_FAILED(tmp)) {
11070 rv = tmp;
11073 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11074 "WaitForScriptButton",
11075 waitButton);
11076 if (NS_FAILED(tmp)) {
11077 rv = tmp;
11080 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11081 "DontAskAgain",
11082 neverShowDlg);
11083 if (NS_FAILED(tmp)) {
11084 rv = tmp;
11088 if (showDebugButton) {
11089 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11090 "DebugScriptButton",
11091 debugButton);
11092 if (NS_FAILED(tmp)) {
11093 rv = tmp;
11096 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11097 "KillScriptWithDebugMessage",
11098 msg);
11099 if (NS_FAILED(tmp)) {
11100 rv = tmp;
11103 else {
11104 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11105 "KillScriptMessage",
11106 msg);
11107 if (NS_FAILED(tmp)) {
11108 rv = tmp;
11112 // GetStringFromName can return NS_OK and still give nullptr string
11113 if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
11114 (!debugButton && showDebugButton) || !neverShowDlg) {
11115 NS_ERROR("Failed to get localized strings.");
11116 return ContinueSlowScript;
11119 // Append file and line number information, if available
11120 if (filename.get()) {
11121 nsXPIDLString scriptLocation;
11122 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
11123 const char16_t *formatParams[] = { filenameUTF16.get() };
11124 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
11125 "KillScriptLocation",
11126 formatParams,
11127 scriptLocation);
11129 if (NS_SUCCEEDED(rv) && scriptLocation) {
11130 msg.AppendLiteral("\n\n");
11131 msg.Append(scriptLocation);
11132 msg.Append(':');
11133 msg.AppendInt(lineno);
11137 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
11138 bool neverShowDlgChk = false;
11139 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
11140 (nsIPrompt::BUTTON_TITLE_IS_STRING *
11141 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
11143 // Add a third button if necessary.
11144 if (showDebugButton)
11145 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
11147 // Null out the operation callback while we're re-entering JS here.
11148 JSRuntime* rt = JS_GetRuntime(cx);
11149 JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr);
11151 // Open the dialog.
11152 rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
11153 debugButton, neverShowDlg, &neverShowDlgChk,
11154 &buttonPressed);
11156 JS_SetInterruptCallback(rt, old);
11158 if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
11159 return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
11161 if (buttonPressed == 2) {
11162 if (debugCallback) {
11163 rv = debugCallback->HandleSlowScriptDebug(this);
11164 return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
11167 JS_ClearPendingException(cx);
11168 return KillSlowScript;
11171 uint32_t
11172 nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver)
11174 MOZ_ASSERT(IsInnerWindow());
11175 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
11177 uint32_t i = 0;
11178 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
11179 while (iter.HasMore()) {
11180 IdleObserverHolder& idleObserver = iter.GetNext();
11181 if (idleObserver.mTimeInS > aIdleObserver->mTimeInS) {
11182 break;
11184 i++;
11185 MOZ_ASSERT(i <= mIdleObservers.Length(), "Array index out of bounds error.");
11188 return i;
11191 nsresult
11192 nsGlobalWindow::RegisterIdleObserver(nsIIdleObserver* aIdleObserver)
11194 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
11196 nsresult rv;
11197 if (mIdleObservers.IsEmpty()) {
11198 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1", &rv);
11199 NS_ENSURE_SUCCESS(rv, rv);
11201 rv = mIdleService->AddIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
11202 NS_ENSURE_SUCCESS(rv, rv);
11204 if (!mIdleTimer) {
11205 mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
11206 NS_ENSURE_SUCCESS(rv, rv);
11207 } else {
11208 mIdleTimer->Cancel();
11212 MOZ_ASSERT(mIdleService);
11213 MOZ_ASSERT(mIdleTimer);
11215 IdleObserverHolder tmpIdleObserver;
11216 tmpIdleObserver.mIdleObserver = aIdleObserver;
11217 rv = aIdleObserver->GetTime(&tmpIdleObserver.mTimeInS);
11218 NS_ENSURE_SUCCESS(rv, rv);
11219 NS_ENSURE_ARG_MAX(tmpIdleObserver.mTimeInS, UINT32_MAX / 1000);
11220 NS_ENSURE_ARG_MIN(tmpIdleObserver.mTimeInS, MIN_IDLE_NOTIFICATION_TIME_S);
11222 uint32_t insertAtIndex = FindInsertionIndex(&tmpIdleObserver);
11223 if (insertAtIndex == mIdleObservers.Length()) {
11224 mIdleObservers.AppendElement(tmpIdleObserver);
11226 else {
11227 mIdleObservers.InsertElementAt(insertAtIndex, tmpIdleObserver);
11230 bool userIsIdle = false;
11231 rv = nsContentUtils::IsUserIdle(MIN_IDLE_NOTIFICATION_TIME_S, &userIsIdle);
11232 NS_ENSURE_SUCCESS(rv, rv);
11234 // Special case. First idle observer added to empty list while the user is idle.
11235 // Haven't received 'idle' topic notification from slow idle service yet.
11236 // Need to wait for the idle notification and then notify idle observers in the list.
11237 if (userIsIdle && mIdleCallbackIndex == -1) {
11238 return NS_OK;
11241 if (!mCurrentlyIdle) {
11242 return NS_OK;
11245 MOZ_ASSERT(mIdleCallbackIndex >= 0);
11247 if (static_cast<int32_t>(insertAtIndex) < mIdleCallbackIndex) {
11248 IdleObserverHolder& idleObserver = mIdleObservers.ElementAt(insertAtIndex);
11249 NotifyIdleObserver(&idleObserver, true);
11250 mIdleCallbackIndex++;
11251 return NS_OK;
11254 if (static_cast<int32_t>(insertAtIndex) == mIdleCallbackIndex) {
11255 mIdleTimer->Cancel();
11256 rv = ScheduleNextIdleObserverCallback();
11257 NS_ENSURE_SUCCESS(rv, rv);
11259 return NS_OK;
11262 nsresult
11263 nsGlobalWindow::FindIndexOfElementToRemove(nsIIdleObserver* aIdleObserver,
11264 int32_t* aRemoveElementIndex)
11266 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
11267 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
11269 *aRemoveElementIndex = 0;
11270 if (mIdleObservers.IsEmpty()) {
11271 return NS_ERROR_FAILURE;
11274 uint32_t aIdleObserverTimeInS;
11275 nsresult rv = aIdleObserver->GetTime(&aIdleObserverTimeInS);
11276 NS_ENSURE_SUCCESS(rv, rv);
11277 NS_ENSURE_ARG_MIN(aIdleObserverTimeInS, MIN_IDLE_NOTIFICATION_TIME_S);
11279 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
11280 while (iter.HasMore()) {
11281 IdleObserverHolder& idleObserver = iter.GetNext();
11282 if (idleObserver.mTimeInS == aIdleObserverTimeInS &&
11283 idleObserver.mIdleObserver == aIdleObserver ) {
11284 break;
11286 (*aRemoveElementIndex)++;
11288 return static_cast<uint32_t>(*aRemoveElementIndex) >= mIdleObservers.Length() ?
11289 NS_ERROR_FAILURE : NS_OK;
11292 nsresult
11293 nsGlobalWindow::UnregisterIdleObserver(nsIIdleObserver* aIdleObserver)
11295 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
11297 int32_t removeElementIndex;
11298 nsresult rv = FindIndexOfElementToRemove(aIdleObserver, &removeElementIndex);
11299 if (NS_FAILED(rv)) {
11300 NS_WARNING("Idle observer not found in list of idle observers. No idle observer removed.");
11301 return NS_OK;
11303 mIdleObservers.RemoveElementAt(removeElementIndex);
11305 MOZ_ASSERT(mIdleTimer);
11306 if (mIdleObservers.IsEmpty() && mIdleService) {
11307 rv = mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
11308 NS_ENSURE_SUCCESS(rv, rv);
11309 mIdleService = nullptr;
11311 mIdleTimer->Cancel();
11312 mIdleCallbackIndex = -1;
11313 return NS_OK;
11316 if (!mCurrentlyIdle) {
11317 return NS_OK;
11320 if (removeElementIndex < mIdleCallbackIndex) {
11321 mIdleCallbackIndex--;
11322 return NS_OK;
11325 if (removeElementIndex != mIdleCallbackIndex) {
11326 return NS_OK;
11329 mIdleTimer->Cancel();
11331 // If the last element in the array had been notified then decrement
11332 // mIdleCallbackIndex because an idle was removed from the list of
11333 // idle observers.
11334 // Example: add idle observer with time 1, 2, 3,
11335 // Idle notifications for idle observers with time 1, 2, 3 are complete
11336 // Remove idle observer with time 3 while the user is still idle.
11337 // The user never transitioned to active state.
11338 // Add an idle observer with idle time 4
11339 if (static_cast<uint32_t>(mIdleCallbackIndex) == mIdleObservers.Length()) {
11340 mIdleCallbackIndex--;
11342 rv = ScheduleNextIdleObserverCallback();
11343 NS_ENSURE_SUCCESS(rv, rv);
11345 return NS_OK;
11348 nsresult
11349 nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
11350 const char16_t* aData)
11352 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
11353 if (IsFrozen()) {
11354 // if an even number of notifications arrive while we're frozen,
11355 // we don't need to fire.
11356 mFireOfflineStatusChangeEventOnThaw = !mFireOfflineStatusChangeEventOnThaw;
11357 } else {
11358 FireOfflineStatusEvent();
11360 return NS_OK;
11363 if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE)) {
11364 mCurrentlyIdle = true;
11365 if (IsFrozen()) {
11366 // need to fire only one idle event while the window is frozen.
11367 mNotifyIdleObserversIdleOnThaw = true;
11368 mNotifyIdleObserversActiveOnThaw = false;
11369 } else if (IsCurrentInnerWindow()) {
11370 HandleIdleActiveEvent();
11372 return NS_OK;
11375 if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) {
11376 mCurrentlyIdle = false;
11377 if (IsFrozen()) {
11378 mNotifyIdleObserversActiveOnThaw = true;
11379 mNotifyIdleObserversIdleOnThaw = false;
11380 } else if (IsCurrentInnerWindow()) {
11381 MOZ_ASSERT(IsInnerWindow());
11382 ScheduleActiveTimerCallback();
11384 return NS_OK;
11387 if (!nsCRT::strcmp(aTopic, "dom-storage2-changed")) {
11388 if (!IsInnerWindow() || !IsCurrentInnerWindow()) {
11389 return NS_OK;
11392 nsIPrincipal *principal;
11393 nsresult rv;
11395 nsRefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
11396 if (!event) {
11397 return NS_ERROR_FAILURE;
11400 nsRefPtr<DOMStorage> changingStorage = event->GetStorageArea();
11401 if (!changingStorage) {
11402 return NS_ERROR_FAILURE;
11405 nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
11407 bool fireMozStorageChanged = false;
11408 principal = GetPrincipal();
11409 if (!principal) {
11410 return NS_OK;
11413 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
11414 bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
11415 if (changingStorage->IsPrivate() != isPrivate) {
11416 return NS_OK;
11419 switch (changingStorage->GetType())
11421 case DOMStorage::SessionStorage:
11423 bool check = false;
11425 nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
11426 if (storageManager) {
11427 rv = storageManager->CheckStorage(principal, istorage, &check);
11428 if (NS_FAILED(rv)) {
11429 return rv;
11433 if (!check) {
11434 // This storage event is not coming from our storage or is coming
11435 // from a different docshell, i.e. it is a clone, ignore this event.
11436 return NS_OK;
11439 #ifdef PR_LOGGING
11440 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
11441 PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p",
11442 this, mSessionStorage.get(), changingStorage.get());
11444 #endif
11446 fireMozStorageChanged = mSessionStorage == changingStorage;
11447 break;
11450 case DOMStorage::LocalStorage:
11452 // Allow event fire only for the same principal storages
11453 // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
11454 nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
11456 bool equals = false;
11457 rv = storagePrincipal->Equals(principal, &equals);
11458 NS_ENSURE_SUCCESS(rv, rv);
11460 if (!equals)
11461 return NS_OK;
11463 fireMozStorageChanged = mLocalStorage == changingStorage;
11464 break;
11466 default:
11467 return NS_OK;
11470 // Clone the storage event included in the observer notification. We want
11471 // to dispatch clones rather than the original event.
11472 ErrorResult error;
11473 nsRefPtr<StorageEvent> newEvent =
11474 CloneStorageEvent(fireMozStorageChanged ?
11475 NS_LITERAL_STRING("MozStorageChanged") :
11476 NS_LITERAL_STRING("storage"),
11477 event, error);
11478 if (error.Failed()) {
11479 return error.ErrorCode();
11482 newEvent->SetTrusted(true);
11484 if (fireMozStorageChanged) {
11485 WidgetEvent* internalEvent = newEvent->GetInternalNSEvent();
11486 internalEvent->mFlags.mOnlyChromeDispatch = true;
11489 if (IsFrozen()) {
11490 // This window is frozen, rather than firing the events here,
11491 // store the domain in which the change happened and fire the
11492 // events if we're ever thawed.
11494 mPendingStorageEvents.AppendElement(newEvent);
11495 return NS_OK;
11498 bool defaultActionEnabled;
11499 DispatchEvent(newEvent, &defaultActionEnabled);
11501 return NS_OK;
11504 if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
11505 if (mApplicationCache)
11506 return NS_OK;
11508 // Instantiate the application object now. It observes update belonging to
11509 // this window's document and correctly updates the applicationCache object
11510 // state.
11511 nsCOMPtr<nsIDOMOfflineResourceList> applicationCache;
11512 GetApplicationCache(getter_AddRefs(applicationCache));
11513 nsCOMPtr<nsIObserver> observer = do_QueryInterface(applicationCache);
11514 if (observer)
11515 observer->Observe(aSubject, aTopic, aData);
11517 return NS_OK;
11520 #ifdef MOZ_B2G
11521 if (!nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC) ||
11522 !nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC)) {
11523 MOZ_ASSERT(IsInnerWindow());
11524 if (!IsCurrentInnerWindow()) {
11525 return NS_OK;
11528 nsCOMPtr<nsIDOMEvent> event;
11529 NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
11530 nsresult rv = event->InitEvent(
11531 !nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC)
11532 ? NETWORK_UPLOAD_EVENT_NAME
11533 : NETWORK_DOWNLOAD_EVENT_NAME,
11534 false, false);
11535 NS_ENSURE_SUCCESS(rv, rv);
11537 event->SetTrusted(true);
11539 bool dummy;
11540 return DispatchEvent(event, &dummy);
11542 #endif // MOZ_B2G
11544 if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
11545 MOZ_ASSERT(!NS_strcmp(aData, MOZ_UTF16("intl.accept_languages")));
11546 MOZ_ASSERT(IsInnerWindow());
11548 // The user preferred languages have changed, we need to fire an event on
11549 // Window object and invalidate the cache for navigator.languages. It is
11550 // done for every change which can be a waste of cycles but those should be
11551 // fairly rare.
11552 // We MUST invalidate navigator.languages before sending the event in the
11553 // very likely situation where an event handler will try to read its value.
11555 if (mNavigator) {
11556 NavigatorBinding::ClearCachedLanguageValue(mNavigator);
11557 NavigatorBinding::ClearCachedLanguagesValue(mNavigator);
11560 nsCOMPtr<nsIDOMEvent> event;
11561 NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
11562 nsresult rv = event->InitEvent(NS_LITERAL_STRING("languagechange"), false, false);
11563 NS_ENSURE_SUCCESS(rv, rv);
11565 event->SetTrusted(true);
11567 bool dummy;
11568 return DispatchEvent(event, &dummy);
11571 NS_WARNING("unrecognized topic in nsGlobalWindow::Observe");
11572 return NS_ERROR_FAILURE;
11575 already_AddRefed<StorageEvent>
11576 nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
11577 const nsRefPtr<StorageEvent>& aEvent,
11578 ErrorResult& aRv)
11580 MOZ_ASSERT(IsInnerWindow());
11582 StorageEventInit dict;
11584 dict.mBubbles = aEvent->Bubbles();
11585 dict.mCancelable = aEvent->Cancelable();
11586 aEvent->GetKey(dict.mKey);
11587 aEvent->GetOldValue(dict.mOldValue);
11588 aEvent->GetNewValue(dict.mNewValue);
11589 aEvent->GetUrl(dict.mUrl);
11591 nsRefPtr<DOMStorage> storageArea = aEvent->GetStorageArea();
11592 MOZ_ASSERT(storageArea);
11594 nsRefPtr<DOMStorage> storage;
11595 if (storageArea->GetType() == DOMStorage::LocalStorage) {
11596 storage = GetLocalStorage(aRv);
11597 } else {
11598 MOZ_ASSERT(storageArea->GetType() == DOMStorage::SessionStorage);
11599 storage = GetSessionStorage(aRv);
11602 if (aRv.Failed() || !storage) {
11603 return nullptr;
11606 MOZ_ASSERT(storage);
11607 MOZ_ASSERT(storage->IsForkOf(storageArea));
11609 dict.mStorageArea = storage;
11611 nsRefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
11612 return event.forget();
11615 nsresult
11616 nsGlobalWindow::FireDelayedDOMEvents()
11618 FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
11620 for (uint32_t i = 0, len = mPendingStorageEvents.Length(); i < len; ++i) {
11621 Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr);
11624 if (mApplicationCache) {
11625 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
11628 if (mFireOfflineStatusChangeEventOnThaw) {
11629 mFireOfflineStatusChangeEventOnThaw = false;
11630 FireOfflineStatusEvent();
11633 if (mNotifyIdleObserversIdleOnThaw) {
11634 mNotifyIdleObserversIdleOnThaw = false;
11635 HandleIdleActiveEvent();
11638 if (mNotifyIdleObserversActiveOnThaw) {
11639 mNotifyIdleObserversActiveOnThaw = false;
11640 ScheduleActiveTimerCallback();
11643 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
11644 if (docShell) {
11645 int32_t childCount = 0;
11646 docShell->GetChildCount(&childCount);
11648 for (int32_t i = 0; i < childCount; ++i) {
11649 nsCOMPtr<nsIDocShellTreeItem> childShell;
11650 docShell->GetChildAt(i, getter_AddRefs(childShell));
11651 NS_ASSERTION(childShell, "null child shell");
11653 nsCOMPtr<nsPIDOMWindow> pWin = childShell->GetWindow();
11654 if (pWin) {
11655 nsGlobalWindow *win =
11656 static_cast<nsGlobalWindow*>
11657 (static_cast<nsPIDOMWindow*>(pWin));
11658 win->FireDelayedDOMEvents();
11663 return NS_OK;
11666 //*****************************************************************************
11667 // nsGlobalWindow: Window Control Functions
11668 //*****************************************************************************
11670 nsIDOMWindow *
11671 nsGlobalWindow::GetParentInternal()
11673 if (IsInnerWindow()) {
11674 nsGlobalWindow* outer = GetOuterWindowInternal();
11675 if (!outer) {
11676 NS_WARNING("No outer window available!");
11677 return nullptr;
11679 return outer->GetParentInternal();
11682 nsCOMPtr<nsIDOMWindow> parent;
11683 GetParent(getter_AddRefs(parent));
11685 if (parent && parent != static_cast<nsIDOMWindow *>(this)) {
11686 return parent;
11689 return nullptr;
11692 void
11693 nsGlobalWindow::UnblockScriptedClosing()
11695 MOZ_ASSERT(IsOuterWindow());
11696 mBlockScriptedClosingFlag = false;
11699 class AutoUnblockScriptClosing
11701 private:
11702 nsRefPtr<nsGlobalWindow> mWin;
11703 public:
11704 explicit AutoUnblockScriptClosing(nsGlobalWindow* aWin)
11705 : mWin(aWin)
11707 MOZ_ASSERT(mWin);
11708 MOZ_ASSERT(mWin->IsOuterWindow());
11710 ~AutoUnblockScriptClosing()
11712 void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing;
11713 NS_DispatchToCurrentThread(NS_NewRunnableMethod(mWin, run));
11717 nsresult
11718 nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
11719 const nsAString& aOptions, bool aDialog,
11720 bool aContentModal, bool aCalledNoScript,
11721 bool aDoJSFixups, bool aNavigate,
11722 nsIArray *argv,
11723 nsISupports *aExtraArgument,
11724 nsIPrincipal *aCalleePrincipal,
11725 JSContext *aJSCallerContext,
11726 nsIDOMWindow **aReturn)
11728 MOZ_ASSERT(IsOuterWindow());
11730 #ifdef DEBUG
11731 uint32_t argc = 0;
11732 if (argv)
11733 argv->GetLength(&argc);
11734 #endif
11735 NS_PRECONDITION(!aExtraArgument || (!argv && argc == 0),
11736 "Can't pass in arguments both ways");
11737 NS_PRECONDITION(!aCalledNoScript || (!argv && argc == 0),
11738 "Can't pass JS args when called via the noscript methods");
11739 NS_PRECONDITION(!aJSCallerContext || !aCalledNoScript,
11740 "Shouldn't have caller context when called noscript");
11742 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
11744 // Calls to window.open from script should navigate.
11745 MOZ_ASSERT(aCalledNoScript || aNavigate);
11747 *aReturn = nullptr;
11749 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
11750 if (!chrome) {
11751 // No chrome means we don't want to go through with this open call
11752 // -- see nsIWindowWatcher.idl
11753 return NS_ERROR_NOT_AVAILABLE;
11756 NS_ASSERTION(mDocShell, "Must have docshell here");
11758 // Popups from apps are never blocked.
11759 bool isApp = false;
11760 if (mDoc) {
11761 isApp = mDoc->NodePrincipal()->GetAppStatus() >=
11762 nsIPrincipal::APP_STATUS_INSTALLED;
11765 const bool checkForPopup = !nsContentUtils::IsCallerChrome() &&
11766 !isApp && !aDialog && !WindowExists(aName, !aCalledNoScript);
11768 // Note: it's very important that this be an nsXPIDLCString, since we want
11769 // .get() on it to return nullptr until we write stuff to it. The window
11770 // watcher expects a null URL string if there is no URL to load.
11771 nsXPIDLCString url;
11772 nsresult rv = NS_OK;
11774 // It's important to do this security check before determining whether this
11775 // window opening should be blocked, to ensure that we don't FireAbuseEvents
11776 // for a window opening that wouldn't have succeeded in the first place.
11777 if (!aUrl.IsEmpty()) {
11778 AppendUTF16toUTF8(aUrl, url);
11780 // It's safe to skip the security check below if we're not a dialog
11781 // because window.openDialog is not callable from content script. See bug
11782 // 56851.
11784 // If we're not navigating, we assume that whoever *does* navigate the
11785 // window will do a security check of their own.
11786 if (url.get() && !aDialog && aNavigate)
11787 rv = SecurityCheckURL(url.get());
11790 if (NS_FAILED(rv))
11791 return rv;
11793 PopupControlState abuseLevel = gPopupControlState;
11794 if (checkForPopup) {
11795 abuseLevel = RevisePopupAbuseLevel(abuseLevel);
11796 if (abuseLevel >= openAbused) {
11797 if (aJSCallerContext) {
11798 // If script in some other window is doing a window.open on us and
11799 // it's being blocked, then it's OK to close us afterwards, probably.
11800 // But if we're doing a window.open on ourselves and block the popup,
11801 // prevent this window from closing until after this script terminates
11802 // so that whatever popup blocker UI the app has will be visible.
11803 if (mContext == GetScriptContextFromJSContext(aJSCallerContext)) {
11804 mBlockScriptedClosingFlag = true;
11805 closeUnblocker.emplace(this);
11809 FireAbuseEvents(true, false, aUrl, aName, aOptions);
11810 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
11814 nsCOMPtr<nsIDOMWindow> domReturn;
11816 nsCOMPtr<nsIWindowWatcher> wwatch =
11817 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
11818 NS_ENSURE_TRUE(wwatch, rv);
11820 NS_ConvertUTF16toUTF8 options(aOptions);
11821 NS_ConvertUTF16toUTF8 name(aName);
11823 const char *options_ptr = aOptions.IsEmpty() ? nullptr : options.get();
11824 const char *name_ptr = aName.IsEmpty() ? nullptr : name.get();
11826 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
11827 NS_ENSURE_STATE(pwwatch);
11830 // Reset popup state while opening a window to prevent the
11831 // current state from being active the whole time a modal
11832 // dialog is open.
11833 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
11835 if (!aCalledNoScript) {
11836 // We asserted at the top of this function that aNavigate is true for
11837 // !aCalledNoScript.
11838 rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
11839 /* aCalledFromScript = */ true,
11840 aDialog, aNavigate, nullptr, argv,
11841 getter_AddRefs(domReturn));
11842 } else {
11843 // Force a system caller here so that the window watcher won't screw us
11844 // up. We do NOT want this case looking at the JS context on the stack
11845 // when searching. Compare comments on
11846 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
11848 // Note: Because nsWindowWatcher is so broken, it's actually important
11849 // that we don't force a system caller here, because that screws it up
11850 // when it tries to compute the caller principal to associate with dialog
11851 // arguments. That whole setup just really needs to be rewritten. :-(
11852 Maybe<AutoNoJSAPI> nojsapi;
11853 if (!aContentModal) {
11854 nojsapi.emplace();
11858 rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
11859 /* aCalledFromScript = */ false,
11860 aDialog, aNavigate, nullptr, aExtraArgument,
11861 getter_AddRefs(domReturn));
11866 NS_ENSURE_SUCCESS(rv, rv);
11868 // success!
11870 NS_ENSURE_TRUE(domReturn, NS_OK);
11871 domReturn.swap(*aReturn);
11873 if (aDoJSFixups) {
11874 nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn));
11875 if (!chrome_win) {
11876 // A new non-chrome window was created from a call to
11877 // window.open() from JavaScript, make sure there's a document in
11878 // the new window. We do this by simply asking the new window for
11879 // its document, this will synchronously create an empty document
11880 // if there is no document in the window.
11881 // XXXbz should this just use EnsureInnerWindow()?
11882 #ifdef DEBUG_jst
11884 nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*aReturn));
11885 NS_ASSERTION(pidomwin->GetExtantDoc(), "No document in new window!!!");
11887 #endif
11889 nsCOMPtr<nsIDOMDocument> doc;
11890 (*aReturn)->GetDocument(getter_AddRefs(doc));
11894 if (checkForPopup) {
11895 if (abuseLevel >= openControlled) {
11896 nsGlobalWindow *opened = static_cast<nsGlobalWindow *>(*aReturn);
11897 if (!opened->IsPopupSpamWindow()) {
11898 opened->SetPopupSpamWindow(true);
11899 ++gOpenPopupSpamCount;
11902 if (abuseLevel >= openAbused)
11903 FireAbuseEvents(false, true, aUrl, aName, aOptions);
11906 return rv;
11909 //*****************************************************************************
11910 // nsGlobalWindow: Timeout Functions
11911 //*****************************************************************************
11913 uint32_t sNestingLevel;
11915 nsGlobalWindow*
11916 nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError)
11918 nsGlobalWindow* currentInner;
11919 nsGlobalWindow* forwardTo;
11920 if (IsInnerWindow()) {
11921 nsGlobalWindow* outer = GetOuterWindowInternal();
11922 currentInner = outer ? outer->GetCurrentInnerWindowInternal() : this;
11924 forwardTo = this;
11925 } else {
11926 currentInner = GetCurrentInnerWindowInternal();
11928 // This needs to forward to the inner window, but since the current
11929 // inner may not be the inner in the calling scope, we need to treat
11930 // this specially here as we don't want timeouts registered in a
11931 // dying inner window to get registered and run on the current inner
11932 // window. To get this right, we need to forward this call to the
11933 // inner window that's calling window.setTimeout().
11935 forwardTo = CallerInnerWindow();
11936 if (!forwardTo && nsContentUtils::IsCallerChrome()) {
11937 forwardTo = currentInner;
11939 if (!forwardTo) {
11940 aError.Throw(NS_ERROR_NOT_AVAILABLE);
11941 return nullptr;
11944 // If the caller and the callee share the same outer window, forward to the
11945 // caller inner. Else, we forward to the current inner (e.g. someone is
11946 // calling setTimeout() on a reference to some other window).
11947 if (forwardTo->GetOuterWindow() != this || !forwardTo->IsInnerWindow()) {
11948 if (!currentInner) {
11949 NS_WARNING("No inner window available!");
11950 aError.Throw(NS_ERROR_NOT_INITIALIZED);
11951 return nullptr;
11954 return currentInner;
11958 // If forwardTo is not the window with an active document then we want the
11959 // call to setTimeout/Interval to be a noop, so return null but don't set an
11960 // error.
11961 return forwardTo->HasActiveDocument() ? currentInner : nullptr;
11964 int32_t
11965 nsGlobalWindow::SetTimeout(JSContext* aCx, Function& aFunction,
11966 int32_t aTimeout,
11967 const Sequence<JS::Value>& aArguments,
11968 ErrorResult& aError)
11970 return SetTimeoutOrInterval(aFunction, aTimeout, aArguments, false, aError);
11973 int32_t
11974 nsGlobalWindow::SetTimeout(JSContext* aCx, const nsAString& aHandler,
11975 int32_t aTimeout,
11976 const Sequence<JS::Value>& /* unused */,
11977 ErrorResult& aError)
11979 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
11982 static bool
11983 IsInterval(const Optional<int32_t>& aTimeout, int32_t& aResultTimeout)
11985 if (aTimeout.WasPassed()) {
11986 aResultTimeout = aTimeout.Value();
11987 return true;
11990 // If no interval was specified, treat this like a timeout, to avoid setting
11991 // an interval of 0 milliseconds.
11992 aResultTimeout = 0;
11993 return false;
11996 int32_t
11997 nsGlobalWindow::SetInterval(JSContext* aCx, Function& aFunction,
11998 const Optional<int32_t>& aTimeout,
11999 const Sequence<JS::Value>& aArguments,
12000 ErrorResult& aError)
12002 int32_t timeout;
12003 bool isInterval = IsInterval(aTimeout, timeout);
12004 return SetTimeoutOrInterval(aFunction, timeout, aArguments, isInterval,
12005 aError);
12008 int32_t
12009 nsGlobalWindow::SetInterval(JSContext* aCx, const nsAString& aHandler,
12010 const Optional<int32_t>& aTimeout,
12011 const Sequence<JS::Value>& /* unused */,
12012 ErrorResult& aError)
12014 int32_t timeout;
12015 bool isInterval = IsInterval(aTimeout, timeout);
12016 return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError);
12019 nsresult
12020 nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
12021 int32_t interval,
12022 bool aIsInterval, int32_t *aReturn)
12024 MOZ_ASSERT(IsInnerWindow());
12026 // If we don't have a document (we could have been unloaded since
12027 // the call to setTimeout was made), do nothing.
12028 if (!mDoc) {
12029 return NS_OK;
12032 // Disallow negative intervals. If aIsInterval also disallow 0,
12033 // because we use that as a "don't repeat" flag.
12034 interval = std::max(aIsInterval ? 1 : 0, interval);
12036 // Make sure we don't proceed with an interval larger than our timer
12037 // code can handle. (Note: we already forced |interval| to be non-negative,
12038 // so the uint32_t cast (to avoid compiler warnings) is ok.)
12039 uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE);
12040 if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
12041 interval = maxTimeoutMs;
12044 nsRefPtr<nsTimeout> timeout = new nsTimeout();
12045 timeout->mIsInterval = aIsInterval;
12046 timeout->mInterval = interval;
12047 timeout->mScriptHandler = aHandler;
12049 // Now clamp the actual interval we will use for the timer based on
12050 uint32_t nestingLevel = sNestingLevel + 1;
12051 uint32_t realInterval = interval;
12052 if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
12053 // Don't allow timeouts less than DOMMinTimeoutValue() from
12054 // now...
12055 realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
12058 // Get principal of currently executing code, save for execution of timeout.
12059 // If our principals subsume the subject principal then use the subject
12060 // principal. Otherwise, use our principal to avoid running script in
12061 // elevated principals.
12063 // Note the direction of this test: We don't allow setTimeouts running with
12064 // chrome privileges on content windows, but we do allow setTimeouts running
12065 // with content privileges on chrome windows (where they can't do very much,
12066 // of course).
12067 nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
12068 nsCOMPtr<nsIPrincipal> ourPrincipal = GetPrincipal();
12069 if (ourPrincipal->Subsumes(subjectPrincipal)) {
12070 timeout->mPrincipal = subjectPrincipal;
12071 } else {
12072 timeout->mPrincipal = ourPrincipal;
12075 ++gTimeoutsRecentlySet;
12076 TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
12078 if (!IsFrozen() && !mTimeoutsSuspendDepth) {
12079 // If we're not currently frozen, then we set timeout->mWhen to be the
12080 // actual firing time of the timer (i.e., now + delta). We also actually
12081 // create a timer and fire it off.
12083 timeout->mWhen = TimeStamp::Now() + delta;
12085 nsresult rv;
12086 timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
12087 if (NS_FAILED(rv)) {
12088 return rv;
12091 nsRefPtr<nsTimeout> copy = timeout;
12093 rv = timeout->InitTimer(TimerCallback, realInterval);
12094 if (NS_FAILED(rv)) {
12095 return rv;
12098 // The timeout is now also held in the timer's closure.
12099 unused << copy.forget();
12100 } else {
12101 // If we are frozen, however, then we instead simply set
12102 // timeout->mTimeRemaining to be the "time remaining" in the timeout (i.e.,
12103 // the interval itself). We don't create a timer for it, since that will
12104 // happen when we are thawed and the timeout will then get a timer and run
12105 // to completion.
12107 timeout->mTimeRemaining = delta;
12110 timeout->mWindow = this;
12112 if (!aIsInterval) {
12113 timeout->mNestingLevel = nestingLevel;
12116 // No popups from timeouts by default
12117 timeout->mPopupState = openAbused;
12119 if (gRunningTimeoutDepth == 0 && gPopupControlState < openAbused) {
12120 // This timeout is *not* set from another timeout and it's set
12121 // while popups are enabled. Propagate the state to the timeout if
12122 // its delay (interval) is equal to or less than what
12123 // "dom.disable_open_click_delay" is set to (in ms).
12125 int32_t delay =
12126 Preferences::GetInt("dom.disable_open_click_delay");
12128 // This is checking |interval|, not realInterval, on purpose,
12129 // because our lower bound for |realInterval| could be pretty high
12130 // in some cases.
12131 if (interval <= delay) {
12132 timeout->mPopupState = gPopupControlState;
12136 InsertTimeoutIntoList(timeout);
12138 timeout->mPublicId = ++mTimeoutPublicIdCounter;
12139 *aReturn = timeout->mPublicId;
12141 return NS_OK;
12145 nsresult
12146 nsGlobalWindow::SetTimeoutOrInterval(bool aIsInterval, int32_t *aReturn)
12148 // This needs to forward to the inner window, but since the current
12149 // inner may not be the inner in the calling scope, we need to treat
12150 // this specially here as we don't want timeouts registered in a
12151 // dying inner window to get registered and run on the current inner
12152 // window. To get this right, we need to forward this call to the
12153 // inner window that's calling window.setTimeout().
12155 if (IsOuterWindow()) {
12156 nsGlobalWindow* callerInner = CallerInnerWindow();
12157 NS_ENSURE_TRUE(callerInner || nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
12159 // If the caller and the callee share the same outer window,
12160 // forward to the callee inner. Else, we forward to the current
12161 // inner (e.g. someone is calling setTimeout() on a reference to
12162 // some other window).
12164 if (callerInner &&
12165 callerInner->GetOuterWindow() == this &&
12166 callerInner->IsInnerWindow()) {
12167 return callerInner->SetTimeoutOrInterval(aIsInterval, aReturn);
12170 FORWARD_TO_INNER(SetTimeoutOrInterval, (aIsInterval, aReturn),
12171 NS_ERROR_NOT_INITIALIZED);
12174 int32_t interval = 0;
12175 bool isInterval = aIsInterval;
12176 nsCOMPtr<nsIScriptTimeoutHandler> handler;
12177 nsresult rv = NS_CreateJSTimeoutHandler(this,
12178 &isInterval,
12179 &interval,
12180 getter_AddRefs(handler));
12181 if (!handler) {
12182 *aReturn = 0;
12183 return rv;
12186 return SetTimeoutOrInterval(handler, interval, isInterval, aReturn);
12189 int32_t
12190 nsGlobalWindow::SetTimeoutOrInterval(Function& aFunction, int32_t aTimeout,
12191 const Sequence<JS::Value>& aArguments,
12192 bool aIsInterval, ErrorResult& aError)
12194 nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError);
12195 if (!inner) {
12196 return -1;
12199 if (inner != this) {
12200 return inner->SetTimeoutOrInterval(aFunction, aTimeout, aArguments,
12201 aIsInterval, aError);
12204 nsCOMPtr<nsIScriptTimeoutHandler> handler =
12205 NS_CreateJSTimeoutHandler(this, aFunction, aArguments, aError);
12206 if (!handler) {
12207 return 0;
12210 int32_t result;
12211 aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
12212 return result;
12215 int32_t
12216 nsGlobalWindow::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
12217 int32_t aTimeout, bool aIsInterval,
12218 ErrorResult& aError)
12220 nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError);
12221 if (!inner) {
12222 return -1;
12225 if (inner != this) {
12226 return inner->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
12227 aError);
12230 nsCOMPtr<nsIScriptTimeoutHandler> handler =
12231 NS_CreateJSTimeoutHandler(aCx, this, aHandler, aError);
12232 if (!handler) {
12233 return 0;
12236 int32_t result;
12237 aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
12238 return result;
12241 bool
12242 nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
12243 nsIScriptContext* aScx)
12245 // Hold on to the timeout in case mExpr or mFunObj releases its
12246 // doc.
12247 nsRefPtr<nsTimeout> timeout = aTimeout;
12248 nsTimeout* last_running_timeout = mRunningTimeout;
12249 mRunningTimeout = timeout;
12250 timeout->mRunning = true;
12252 // Push this timeout's popup control state, which should only be
12253 // eabled the first time a timeout fires that was created while
12254 // popups were enabled and with a delay less than
12255 // "dom.disable_open_click_delay".
12256 nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState);
12258 // Clear the timeout's popup state, if any, to prevent interval
12259 // timeouts from repeatedly opening poups.
12260 timeout->mPopupState = openAbused;
12262 ++gRunningTimeoutDepth;
12263 ++mTimeoutFiringDepth;
12265 bool trackNestingLevel = !timeout->mIsInterval;
12266 uint32_t nestingLevel;
12267 if (trackNestingLevel) {
12268 nestingLevel = sNestingLevel;
12269 sNestingLevel = timeout->mNestingLevel;
12272 nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
12273 nsRefPtr<Function> callback = handler->GetCallback();
12274 if (!callback) {
12275 // Evaluate the timeout expression.
12276 const char16_t* script = handler->GetHandlerText();
12277 NS_ASSERTION(script, "timeout has no script nor handler text!");
12279 const char* filename = nullptr;
12280 uint32_t lineNo = 0;
12281 handler->GetLocation(&filename, &lineNo);
12283 // New script entry point required, due to the "Create a script" sub-step of
12284 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialization-steps
12285 AutoEntryScript entryScript(this, true, aScx->GetNativeContext());
12286 JS::CompileOptions options(entryScript.cx());
12287 options.setFileAndLine(filename, lineNo)
12288 .setVersion(JSVERSION_DEFAULT);
12289 JS::Rooted<JSObject*> global(entryScript.cx(), FastGetGlobalJSObject());
12290 nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script),
12291 global, options);
12292 } else {
12293 // Hold strong ref to ourselves while we call the callback.
12294 nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
12295 ErrorResult ignored;
12296 JS::Rooted<JS::Value> ignoredVal(CycleCollectedJSRuntime::Get()->Runtime());
12297 callback->Call(me, handler->GetArgs(), &ignoredVal, ignored);
12300 // We ignore any failures from calling EvaluateString() on the context or
12301 // Call() on a Function here since we're in a loop
12302 // where we're likely to be running timeouts whose OS timers
12303 // didn't fire in time and we don't want to not fire those timers
12304 // now just because execution of one timer failed. We can't
12305 // propagate the error to anyone who cares about it from this
12306 // point anyway, and the script context should have already reported
12307 // the script error in the usual way - so we just drop it.
12309 if (trackNestingLevel) {
12310 sNestingLevel = nestingLevel;
12313 --mTimeoutFiringDepth;
12314 --gRunningTimeoutDepth;
12316 mRunningTimeout = last_running_timeout;
12317 timeout->mRunning = false;
12318 return timeout->mCleared;
12321 bool
12322 nsGlobalWindow::RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
12323 bool aRunningPendingTimeouts)
12325 if (!aTimeout->mIsInterval) {
12326 if (aTimeout->mTimer) {
12327 // The timeout still has an OS timer, and it's not an interval,
12328 // that means that the OS timer could still fire; cancel the OS
12329 // timer and release its reference to the timeout.
12330 aTimeout->mTimer->Cancel();
12331 aTimeout->mTimer = nullptr;
12332 aTimeout->Release();
12334 return false;
12337 // Compute time to next timeout for interval timer.
12338 // Make sure nextInterval is at least DOMMinTimeoutValue().
12339 TimeDuration nextInterval =
12340 TimeDuration::FromMilliseconds(std::max(aTimeout->mInterval,
12341 uint32_t(DOMMinTimeoutValue())));
12343 // If we're running pending timeouts, set the next interval to be
12344 // relative to "now", and not to when the timeout that was pending
12345 // should have fired.
12346 TimeStamp firingTime;
12347 if (aRunningPendingTimeouts) {
12348 firingTime = now + nextInterval;
12349 } else {
12350 firingTime = aTimeout->mWhen + nextInterval;
12353 TimeStamp currentNow = TimeStamp::Now();
12354 TimeDuration delay = firingTime - currentNow;
12356 // And make sure delay is nonnegative; that might happen if the timer
12357 // thread is firing our timers somewhat early or if they're taking a long
12358 // time to run the callback.
12359 if (delay < TimeDuration(0)) {
12360 delay = TimeDuration(0);
12363 if (!aTimeout->mTimer) {
12364 NS_ASSERTION(IsFrozen() || mTimeoutsSuspendDepth,
12365 "How'd our timer end up null if we're not frozen or "
12366 "suspended?");
12368 aTimeout->mTimeRemaining = delay;
12369 return true;
12372 aTimeout->mWhen = currentNow + delay;
12374 // Reschedule the OS timer. Don't bother returning any error codes if
12375 // this fails since the callers of this method don't care about them.
12376 nsresult rv = aTimeout->InitTimer(TimerCallback, delay.ToMilliseconds());
12378 if (NS_FAILED(rv)) {
12379 NS_ERROR("Error initializing timer for DOM timeout!");
12381 // We failed to initialize the new OS timer, this timer does
12382 // us no good here so we just cancel it (just in case) and
12383 // null out the pointer to the OS timer, this will release the
12384 // OS timer. As we continue executing the code below we'll end
12385 // up deleting the timeout since it's not an interval timeout
12386 // any more (since timeout->mTimer == nullptr).
12387 aTimeout->mTimer->Cancel();
12388 aTimeout->mTimer = nullptr;
12390 // Now that the OS timer no longer has a reference to the
12391 // timeout we need to drop that reference.
12392 aTimeout->Release();
12394 return false;
12397 return true;
12400 void
12401 nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
12403 // If a modal dialog is open for this window, return early. Pending
12404 // timeouts will run when the modal dialog is dismissed.
12405 if (IsInModalState() || mTimeoutsSuspendDepth) {
12406 return;
12409 NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
12410 NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");
12412 nsTimeout *nextTimeout;
12413 nsTimeout *last_expired_timeout, *last_insertion_point;
12414 uint32_t firingDepth = mTimeoutFiringDepth + 1;
12416 // Make sure that the window and the script context don't go away as
12417 // a result of running timeouts
12418 nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(this);
12420 // A native timer has gone off. See which of our timeouts need
12421 // servicing
12422 TimeStamp now = TimeStamp::Now();
12423 TimeStamp deadline;
12425 if (aTimeout && aTimeout->mWhen > now) {
12426 // The OS timer fired early (which can happen due to the timers
12427 // having lower precision than TimeStamp does). Set |deadline| to
12428 // be the time when the OS timer *should* have fired so that any
12429 // timers that *should* have fired before aTimeout *will* be fired
12430 // now.
12432 deadline = aTimeout->mWhen;
12433 } else {
12434 deadline = now;
12437 // The timeout list is kept in deadline order. Discover the latest
12438 // timeout whose deadline has expired. On some platforms, native
12439 // timeout events fire "early", so we need to test the timer as well
12440 // as the deadline.
12441 last_expired_timeout = nullptr;
12442 for (nsTimeout *timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
12443 if (((timeout == aTimeout) || (timeout->mWhen <= deadline)) &&
12444 (timeout->mFiringDepth == 0)) {
12445 // Mark any timeouts that are on the list to be fired with the
12446 // firing depth so that we can reentrantly run timeouts
12447 timeout->mFiringDepth = firingDepth;
12448 last_expired_timeout = timeout;
12452 // Maybe the timeout that the event was fired for has been deleted
12453 // and there are no others timeouts with deadlines that make them
12454 // eligible for execution yet. Go away.
12455 if (!last_expired_timeout) {
12456 return;
12459 // Record telemetry information about timers set recently.
12460 TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL);
12461 if (gLastRecordedRecentTimeouts.IsNull() ||
12462 now - gLastRecordedRecentTimeouts > recordingInterval) {
12463 uint32_t count = gTimeoutsRecentlySet;
12464 gTimeoutsRecentlySet = 0;
12465 Telemetry::Accumulate(Telemetry::DOM_TIMERS_RECENTLY_SET, count);
12466 gLastRecordedRecentTimeouts = now;
12469 // Insert a dummy timeout into the list of timeouts between the
12470 // portion of the list that we are about to process now and those
12471 // timeouts that will be processed in a future call to
12472 // win_run_timeout(). This dummy timeout serves as the head of the
12473 // list for any timeouts inserted as a result of running a timeout.
12474 nsRefPtr<nsTimeout> dummy_timeout = new nsTimeout();
12475 dummy_timeout->mFiringDepth = firingDepth;
12476 dummy_timeout->mWhen = now;
12477 last_expired_timeout->setNext(dummy_timeout);
12478 dummy_timeout->AddRef();
12480 last_insertion_point = mTimeoutInsertionPoint;
12481 // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
12482 // the logic in ResetTimersForNonBackgroundWindow will need to change.
12483 mTimeoutInsertionPoint = dummy_timeout;
12485 Telemetry::AutoCounter<Telemetry::DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT> timeoutsRan;
12487 for (nsTimeout *timeout = mTimeouts.getFirst();
12488 timeout != dummy_timeout && !IsFrozen();
12489 timeout = nextTimeout) {
12490 nextTimeout = timeout->getNext();
12492 if (timeout->mFiringDepth != firingDepth) {
12493 // We skip the timeout since it's on the list to run at another
12494 // depth.
12496 continue;
12499 if (mTimeoutsSuspendDepth) {
12500 // Some timer did suspend us. Make sure the
12501 // rest of the timers get executed later.
12502 timeout->mFiringDepth = 0;
12503 continue;
12506 // The timeout is on the list to run at this depth, go ahead and
12507 // process it.
12509 // Get the script context (a strong ref to prevent it going away)
12510 // for this timeout and ensure the script language is enabled.
12511 nsCOMPtr<nsIScriptContext> scx = GetContextInternal();
12513 if (!scx) {
12514 // No context means this window was closed or never properly
12515 // initialized for this language.
12516 continue;
12519 // This timeout is good to run
12520 ++timeoutsRan;
12521 bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
12523 if (timeout_was_cleared) {
12524 // The running timeout's window was cleared, this means that
12525 // ClearAllTimeouts() was called from a *nested* call, possibly
12526 // through a timeout that fired while a modal (to this window)
12527 // dialog was open or through other non-obvious paths.
12528 MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
12530 mTimeoutInsertionPoint = last_insertion_point;
12532 return;
12535 // If we have a regular interval timer, we re-schedule the
12536 // timeout, accounting for clock drift.
12537 bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
12539 // Running a timeout can cause another timeout to be deleted, so
12540 // we need to reset the pointer to the following timeout.
12541 nextTimeout = timeout->getNext();
12543 timeout->remove();
12545 if (needsReinsertion) {
12546 // Insert interval timeout onto list sorted in deadline order.
12547 // AddRefs timeout.
12548 InsertTimeoutIntoList(timeout);
12551 // Release the timeout struct since it's possibly out of the list
12552 timeout->Release();
12555 // Take the dummy timeout off the head of the list
12556 dummy_timeout->remove();
12557 dummy_timeout->Release();
12558 MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
12560 mTimeoutInsertionPoint = last_insertion_point;
12563 void
12564 nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID, ErrorResult& aError)
12566 FORWARD_TO_INNER_OR_THROW(ClearTimeoutOrInterval, (aTimerID, aError),
12567 aError, );
12569 uint32_t public_id = (uint32_t)aTimerID;
12570 nsTimeout *timeout;
12572 for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
12573 if (timeout->mPublicId == public_id) {
12574 if (timeout->mRunning) {
12575 /* We're running from inside the timeout. Mark this
12576 timeout for deferred deletion by the code in
12577 RunTimeout() */
12578 timeout->mIsInterval = false;
12580 else {
12581 /* Delete the timeout from the pending timeout list */
12582 timeout->remove();
12584 if (timeout->mTimer) {
12585 timeout->mTimer->Cancel();
12586 timeout->mTimer = nullptr;
12587 timeout->Release();
12589 timeout->Release();
12591 break;
12596 nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
12598 FORWARD_TO_INNER(ResetTimersForNonBackgroundWindow, (),
12599 NS_ERROR_NOT_INITIALIZED);
12601 if (IsFrozen() || mTimeoutsSuspendDepth) {
12602 return NS_OK;
12605 TimeStamp now = TimeStamp::Now();
12607 // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
12608 // timers and the timers we're planning to fire all come before
12609 // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
12610 // with an mWhen that may be semi-bogus. In that case, we don't need to do
12611 // anything with mTimeoutInsertionPoint or anything before it, so should
12612 // start at the timer after mTimeoutInsertionPoint, if there is one.
12613 // Otherwise, start at the beginning of the list.
12614 for (nsTimeout *timeout = mTimeoutInsertionPoint ?
12615 mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
12616 timeout; ) {
12617 // It's important that this check be <= so that we guarantee that
12618 // taking std::max with |now| won't make a quantity equal to
12619 // timeout->mWhen below.
12620 if (timeout->mWhen <= now) {
12621 timeout = timeout->getNext();
12622 continue;
12625 if (timeout->mWhen - now >
12626 TimeDuration::FromMilliseconds(gMinBackgroundTimeoutValue)) {
12627 // No need to loop further. Timeouts are sorted in mWhen order
12628 // and the ones after this point were all set up for at least
12629 // gMinBackgroundTimeoutValue ms and hence were not clamped.
12630 break;
12633 /* We switched from background. Re-init the timer appropriately */
12634 // Compute the interval the timer should have had if it had not been set in a
12635 // background window
12636 TimeDuration interval =
12637 TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
12638 uint32_t(DOMMinTimeoutValue())));
12639 uint32_t oldIntervalMillisecs = 0;
12640 timeout->mTimer->GetDelay(&oldIntervalMillisecs);
12641 TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
12642 if (oldInterval > interval) {
12643 // unclamp
12644 TimeStamp firingTime =
12645 std::max(timeout->mWhen - oldInterval + interval, now);
12647 NS_ASSERTION(firingTime < timeout->mWhen,
12648 "Our firing time should strictly decrease!");
12650 TimeDuration delay = firingTime - now;
12651 timeout->mWhen = firingTime;
12653 // Since we reset mWhen we need to move |timeout| to the right
12654 // place in the list so that it remains sorted by mWhen.
12656 // Get the pointer to the next timeout now, before we move the
12657 // current timeout in the list.
12658 nsTimeout* nextTimeout = timeout->getNext();
12660 // It is safe to remove and re-insert because mWhen is now
12661 // strictly smaller than it used to be, so we know we'll insert
12662 // |timeout| before nextTimeout.
12663 NS_ASSERTION(!nextTimeout ||
12664 timeout->mWhen < nextTimeout->mWhen, "How did that happen?");
12665 timeout->remove();
12666 // InsertTimeoutIntoList will addref |timeout| and reset
12667 // mFiringDepth. Make sure to undo that after calling it.
12668 uint32_t firingDepth = timeout->mFiringDepth;
12669 InsertTimeoutIntoList(timeout);
12670 timeout->mFiringDepth = firingDepth;
12671 timeout->Release();
12673 nsresult rv = timeout->InitTimer(TimerCallback, delay.ToMilliseconds());
12675 if (NS_FAILED(rv)) {
12676 NS_WARNING("Error resetting non background timer for DOM timeout!");
12677 return rv;
12680 timeout = nextTimeout;
12681 } else {
12682 timeout = timeout->getNext();
12686 return NS_OK;
12689 void
12690 nsGlobalWindow::ClearAllTimeouts()
12692 nsTimeout *timeout, *nextTimeout;
12694 for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
12695 /* If RunTimeout() is higher up on the stack for this
12696 window, e.g. as a result of document.write from a timeout,
12697 then we need to reset the list insertion point for
12698 newly-created timeouts in case the user adds a timeout,
12699 before we pop the stack back to RunTimeout. */
12700 if (mRunningTimeout == timeout)
12701 mTimeoutInsertionPoint = nullptr;
12703 nextTimeout = timeout->getNext();
12705 if (timeout->mTimer) {
12706 timeout->mTimer->Cancel();
12707 timeout->mTimer = nullptr;
12709 // Drop the count since the timer isn't going to hold on
12710 // anymore.
12711 timeout->Release();
12714 // Set timeout->mCleared to true to indicate that the timeout was
12715 // cleared and taken out of the list of timeouts
12716 timeout->mCleared = true;
12718 // Drop the count since we're removing it from the list.
12719 timeout->Release();
12722 // Clear out our list
12723 mTimeouts.clear();
12726 void
12727 nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout)
12729 NS_ASSERTION(IsInnerWindow(),
12730 "InsertTimeoutIntoList() called on outer window!");
12732 // Start at mLastTimeout and go backwards. Don't go further than
12733 // mTimeoutInsertionPoint, though. This optimizes for the common case of
12734 // insertion at the end.
12735 nsTimeout* prevSibling;
12736 for (prevSibling = mTimeouts.getLast();
12737 prevSibling && prevSibling != mTimeoutInsertionPoint &&
12738 // This condition needs to match the one in SetTimeoutOrInterval that
12739 // determines whether to set mWhen or mTimeRemaining.
12740 ((IsFrozen() || mTimeoutsSuspendDepth) ?
12741 prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
12742 prevSibling->mWhen > aTimeout->mWhen);
12743 prevSibling = prevSibling->getPrevious()) {
12744 /* Do nothing; just searching */
12747 // Now link in aTimeout after prevSibling.
12748 if (prevSibling) {
12749 prevSibling->setNext(aTimeout);
12750 } else {
12751 mTimeouts.insertFront(aTimeout);
12754 aTimeout->mFiringDepth = 0;
12756 // Increment the timeout's reference count since it's now held on to
12757 // by the list
12758 aTimeout->AddRef();
12761 // static
12762 void
12763 nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure)
12765 nsRefPtr<nsTimeout> timeout = (nsTimeout *)aClosure;
12767 timeout->mWindow->RunTimeout(timeout);
12770 //*****************************************************************************
12771 // nsGlobalWindow: Helper Functions
12772 //*****************************************************************************
12774 already_AddRefed<nsIDocShellTreeOwner>
12775 nsGlobalWindow::GetTreeOwner()
12777 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
12779 // If there's no docShellAsItem, this window must have been closed,
12780 // in that case there is no tree owner.
12782 if (!mDocShell) {
12783 return nullptr;
12786 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
12787 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
12788 return treeOwner.forget();
12791 already_AddRefed<nsIBaseWindow>
12792 nsGlobalWindow::GetTreeOwnerWindow()
12794 MOZ_ASSERT(IsOuterWindow());
12796 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
12798 // If there's no mDocShell, this window must have been closed,
12799 // in that case there is no tree owner.
12801 if (mDocShell) {
12802 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
12805 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
12806 return baseWindow.forget();
12809 already_AddRefed<nsIWebBrowserChrome>
12810 nsGlobalWindow::GetWebBrowserChrome()
12812 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
12814 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
12815 return browserChrome.forget();
12818 nsIScrollableFrame *
12819 nsGlobalWindow::GetScrollFrame()
12821 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
12823 if (!mDocShell) {
12824 return nullptr;
12827 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
12828 if (presShell) {
12829 return presShell->GetRootScrollFrameAsScrollable();
12831 return nullptr;
12834 nsresult
12835 nsGlobalWindow::SecurityCheckURL(const char *aURL)
12837 nsCOMPtr<nsPIDOMWindow> sourceWindow = do_QueryInterface(GetEntryGlobal());
12838 if (!sourceWindow) {
12839 sourceWindow = this;
12841 AutoJSContext cx;
12842 nsGlobalWindow* sourceWin = static_cast<nsGlobalWindow*>(sourceWindow.get());
12843 JSAutoCompartment ac(cx, sourceWin->GetGlobalJSObject());
12845 // Resolve the baseURI, which could be relative to the calling window.
12847 // Note the algorithm to get the base URI should match the one
12848 // used to actually kick off the load in nsWindowWatcher.cpp.
12849 nsCOMPtr<nsIDocument> doc = sourceWindow->GetDoc();
12850 nsIURI* baseURI = nullptr;
12851 nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8
12852 if (doc) {
12853 baseURI = doc->GetDocBaseURI();
12854 charset = doc->GetDocumentCharacterSet();
12856 nsCOMPtr<nsIURI> uri;
12857 nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
12858 charset.get(), baseURI);
12859 if (NS_WARN_IF(NS_FAILED(rv))) {
12860 return rv;
12863 if (NS_FAILED(nsContentUtils::GetSecurityManager()->
12864 CheckLoadURIFromScript(cx, uri))) {
12865 return NS_ERROR_FAILURE;
12868 return NS_OK;
12871 void
12872 nsGlobalWindow::FlushPendingNotifications(mozFlushType aType)
12874 if (mDoc) {
12875 mDoc->FlushPendingNotifications(aType);
12879 void
12880 nsGlobalWindow::EnsureSizeUpToDate()
12882 MOZ_ASSERT(IsOuterWindow());
12884 // If we're a subframe, make sure our size is up to date. It's OK that this
12885 // crosses the content/chrome boundary, since chrome can have pending reflows
12886 // too.
12887 nsGlobalWindow *parent =
12888 static_cast<nsGlobalWindow *>(GetPrivateParent());
12889 if (parent) {
12890 parent->FlushPendingNotifications(Flush_Layout);
12894 already_AddRefed<nsISupports>
12895 nsGlobalWindow::SaveWindowState()
12897 NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state");
12899 if (!mContext || !GetWrapperPreserveColor()) {
12900 // The window may be getting torn down; don't bother saving state.
12901 return nullptr;
12904 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
12905 NS_ASSERTION(inner, "No inner window to save");
12907 // Don't do anything else to this inner window! After this point, all
12908 // calls to SetTimeoutOrInterval will create entries in the timeout
12909 // list that will only run after this window has come out of the bfcache.
12910 // Also, while we're frozen, we won't dispatch online/offline events
12911 // to the page.
12912 inner->Freeze();
12914 nsCOMPtr<nsISupports> state = new WindowStateHolder(mContext, inner);
12916 #ifdef DEBUG_PAGE_CACHE
12917 printf("saving window state, state = %p\n", (void*)state);
12918 #endif
12920 return state.forget();
12923 nsresult
12924 nsGlobalWindow::RestoreWindowState(nsISupports *aState)
12926 NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window");
12928 if (!mContext || !GetWrapperPreserveColor()) {
12929 // The window may be getting torn down; don't bother restoring state.
12930 return NS_OK;
12933 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
12934 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
12936 #ifdef DEBUG_PAGE_CACHE
12937 printf("restoring window state, state = %p\n", (void*)holder);
12938 #endif
12940 // And we're ready to go!
12941 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
12943 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
12944 // it easy to tell which link was last clicked when going back a page.
12945 nsIContent* focusedNode = inner->GetFocusedNode();
12946 if (IsLink(focusedNode)) {
12947 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
12948 if (fm) {
12949 nsCOMPtr<nsIDOMElement> focusedElement(do_QueryInterface(focusedNode));
12950 fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL |
12951 nsIFocusManager::FLAG_SHOWRING);
12955 inner->Thaw();
12957 holder->DidRestoreWindow();
12959 return NS_OK;
12962 void
12963 nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
12964 bool aFreezeChildren)
12966 FORWARD_TO_INNER_VOID(SuspendTimeouts, (aIncrease, aFreezeChildren));
12968 bool suspended = (mTimeoutsSuspendDepth != 0);
12969 mTimeoutsSuspendDepth += aIncrease;
12971 if (!suspended) {
12972 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12973 if (ac) {
12974 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
12975 ac->RemoveWindowListener(mEnabledSensors[i], this);
12977 DisableGamepadUpdates();
12979 // Suspend all of the workers for this window.
12980 mozilla::dom::workers::SuspendWorkersForWindow(this);
12982 TimeStamp now = TimeStamp::Now();
12983 for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
12984 // Set mTimeRemaining to be the time remaining for this timer.
12985 if (t->mWhen > now)
12986 t->mTimeRemaining = t->mWhen - now;
12987 else
12988 t->mTimeRemaining = TimeDuration(0);
12990 // Drop the XPCOM timer; we'll reschedule when restoring the state.
12991 if (t->mTimer) {
12992 t->mTimer->Cancel();
12993 t->mTimer = nullptr;
12995 // Drop the reference that the timer's closure had on this timeout, we'll
12996 // add it back in ResumeTimeouts. Note that it shouldn't matter that we're
12997 // passing null for the context, since this shouldn't actually release this
12998 // timeout.
12999 t->Release();
13003 // Suspend all of the AudioContexts for this window
13004 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
13005 mAudioContexts[i]->Suspend();
13009 // Suspend our children as well.
13010 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
13011 if (docShell) {
13012 int32_t childCount = 0;
13013 docShell->GetChildCount(&childCount);
13015 for (int32_t i = 0; i < childCount; ++i) {
13016 nsCOMPtr<nsIDocShellTreeItem> childShell;
13017 docShell->GetChildAt(i, getter_AddRefs(childShell));
13018 NS_ASSERTION(childShell, "null child shell");
13020 nsCOMPtr<nsPIDOMWindow> pWin = childShell->GetWindow();
13021 if (pWin) {
13022 nsGlobalWindow *win =
13023 static_cast<nsGlobalWindow*>
13024 (static_cast<nsPIDOMWindow*>(pWin));
13025 NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
13026 nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
13028 // This is a bit hackish. Only freeze/suspend windows which are truly our
13029 // subwindows.
13030 nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
13031 if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
13032 continue;
13035 win->SuspendTimeouts(aIncrease, aFreezeChildren);
13037 if (inner && aFreezeChildren) {
13038 inner->Freeze();
13045 nsresult
13046 nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
13048 FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED);
13050 NS_ASSERTION(mTimeoutsSuspendDepth, "Mismatched calls to ResumeTimeouts!");
13051 --mTimeoutsSuspendDepth;
13052 bool shouldResume = (mTimeoutsSuspendDepth == 0) && !mInnerObjectsFreed;
13053 nsresult rv;
13055 if (shouldResume) {
13056 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
13057 if (ac) {
13058 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
13059 ac->AddWindowListener(mEnabledSensors[i], this);
13061 EnableGamepadUpdates();
13063 // Resume all of the AudioContexts for this window
13064 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
13065 mAudioContexts[i]->Resume();
13068 // Resume all of the workers for this window.
13069 mozilla::dom::workers::ResumeWorkersForWindow(this);
13071 // Restore all of the timeouts, using the stored time remaining
13072 // (stored in timeout->mTimeRemaining).
13074 TimeStamp now = TimeStamp::Now();
13076 #ifdef DEBUG
13077 bool _seenDummyTimeout = false;
13078 #endif
13080 for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
13081 // There's a chance we're being called with RunTimeout on the stack in which
13082 // case we have a dummy timeout in the list that *must not* be resumed. It
13083 // can be identified by a null mWindow.
13084 if (!t->mWindow) {
13085 #ifdef DEBUG
13086 NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
13087 _seenDummyTimeout = true;
13088 #endif
13089 continue;
13092 // XXXbz the combination of the way |delay| and |t->mWhen| are set here
13093 // makes no sense. Are we trying to impose that min timeout value or
13094 // not???
13095 uint32_t delay =
13096 std::max(int32_t(t->mTimeRemaining.ToMilliseconds()),
13097 DOMMinTimeoutValue());
13099 // Set mWhen back to the time when the timer is supposed to
13100 // fire.
13101 t->mWhen = now + t->mTimeRemaining;
13103 t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
13104 NS_ENSURE_TRUE(t->mTimer, NS_ERROR_OUT_OF_MEMORY);
13106 rv = t->InitTimer(TimerCallback, delay);
13107 if (NS_FAILED(rv)) {
13108 t->mTimer = nullptr;
13109 return rv;
13112 // Add a reference for the new timer's closure.
13113 t->AddRef();
13117 // Resume our children as well.
13118 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
13119 if (docShell) {
13120 int32_t childCount = 0;
13121 docShell->GetChildCount(&childCount);
13123 for (int32_t i = 0; i < childCount; ++i) {
13124 nsCOMPtr<nsIDocShellTreeItem> childShell;
13125 docShell->GetChildAt(i, getter_AddRefs(childShell));
13126 NS_ASSERTION(childShell, "null child shell");
13128 nsCOMPtr<nsPIDOMWindow> pWin = childShell->GetWindow();
13129 if (pWin) {
13130 nsGlobalWindow *win =
13131 static_cast<nsGlobalWindow*>
13132 (static_cast<nsPIDOMWindow*>(pWin));
13134 NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
13135 nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
13137 // This is a bit hackish. Only thaw/resume windows which are truly our
13138 // subwindows.
13139 nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
13140 if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
13141 continue;
13144 if (inner && aThawChildren) {
13145 inner->Thaw();
13148 rv = win->ResumeTimeouts(aThawChildren);
13149 NS_ENSURE_SUCCESS(rv, rv);
13154 return NS_OK;
13157 uint32_t
13158 nsGlobalWindow::TimeoutSuspendCount()
13160 FORWARD_TO_INNER(TimeoutSuspendCount, (), 0);
13161 return mTimeoutsSuspendDepth;
13164 void
13165 nsGlobalWindow::EnableDeviceSensor(uint32_t aType)
13167 MOZ_ASSERT(IsInnerWindow());
13169 bool alreadyEnabled = false;
13170 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
13171 if (mEnabledSensors[i] == aType) {
13172 alreadyEnabled = true;
13173 break;
13177 mEnabledSensors.AppendElement(aType);
13179 if (alreadyEnabled) {
13180 return;
13183 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
13184 if (ac) {
13185 ac->AddWindowListener(aType, this);
13189 void
13190 nsGlobalWindow::DisableDeviceSensor(uint32_t aType)
13192 MOZ_ASSERT(IsInnerWindow());
13194 int32_t doomedElement = -1;
13195 int32_t listenerCount = 0;
13196 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
13197 if (mEnabledSensors[i] == aType) {
13198 doomedElement = i;
13199 listenerCount++;
13203 if (doomedElement == -1) {
13204 return;
13207 mEnabledSensors.RemoveElementAt(doomedElement);
13209 if (listenerCount > 1) {
13210 return;
13213 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
13214 if (ac) {
13215 ac->RemoveWindowListener(aType, this);
13219 void
13220 nsGlobalWindow::SetHasGamepadEventListener(bool aHasGamepad/* = true*/)
13222 MOZ_ASSERT(IsInnerWindow());
13223 mHasGamepad = aHasGamepad;
13224 if (aHasGamepad) {
13225 EnableGamepadUpdates();
13229 void
13230 nsGlobalWindow::EnableTimeChangeNotifications()
13232 mozilla::time::AddWindowListener(this);
13235 void
13236 nsGlobalWindow::DisableTimeChangeNotifications()
13238 mozilla::time::RemoveWindowListener(this);
13241 static PLDHashOperator
13242 CollectSizeAndListenerCount(
13243 nsPtrHashKey<DOMEventTargetHelper>* aEntry,
13244 void *arg)
13246 nsWindowSizes* windowSizes = static_cast<nsWindowSizes*>(arg);
13248 DOMEventTargetHelper* et = aEntry->GetKey();
13250 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
13251 windowSizes->mDOMEventTargetsSize +=
13252 iSizeOf->SizeOfEventTargetIncludingThis(windowSizes->mMallocSizeOf);
13255 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
13256 windowSizes->mDOMEventListenersCount += elm->ListenerCount();
13259 return PL_DHASH_NEXT;
13262 void
13263 nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
13265 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
13267 if (IsInnerWindow()) {
13268 EventListenerManager* elm = GetExistingListenerManager();
13269 if (elm) {
13270 aWindowSizes->mDOMOtherSize +=
13271 elm->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
13272 aWindowSizes->mDOMEventListenersCount +=
13273 elm->ListenerCount();
13275 if (mDoc) {
13276 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
13280 if (mNavigator) {
13281 aWindowSizes->mDOMOtherSize +=
13282 mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
13285 // The things pointed to by the entries will be measured below, so we
13286 // use nullptr for the callback here.
13287 aWindowSizes->mDOMEventTargetsSize +=
13288 mEventTargetObjects.SizeOfExcludingThis(nullptr,
13289 aWindowSizes->mMallocSizeOf);
13290 aWindowSizes->mDOMEventTargetsCount +=
13291 const_cast<nsTHashtable<nsPtrHashKey<DOMEventTargetHelper> >*>
13292 (&mEventTargetObjects)->EnumerateEntries(CollectSizeAndListenerCount,
13293 aWindowSizes);
13297 #ifdef MOZ_GAMEPAD
13298 void
13299 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
13301 MOZ_ASSERT(IsInnerWindow());
13302 mGamepads.Put(aIndex, aGamepad);
13305 void
13306 nsGlobalWindow::RemoveGamepad(uint32_t aIndex)
13308 MOZ_ASSERT(IsInnerWindow());
13309 mGamepads.Remove(aIndex);
13312 // static
13313 PLDHashOperator
13314 nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData,
13315 void* aUserArg)
13317 nsTArray<nsRefPtr<Gamepad> >* array =
13318 static_cast<nsTArray<nsRefPtr<Gamepad> >*>(aUserArg);
13319 array->EnsureLengthAtLeast(aKey + 1);
13320 (*array)[aKey] = aData;
13321 return PL_DHASH_NEXT;
13324 void
13325 nsGlobalWindow::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads)
13327 MOZ_ASSERT(IsInnerWindow());
13328 aGamepads.Clear();
13329 // mGamepads.Count() may not be sufficient, but it's not harmful.
13330 aGamepads.SetCapacity(mGamepads.Count());
13331 mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads);
13334 already_AddRefed<Gamepad>
13335 nsGlobalWindow::GetGamepad(uint32_t aIndex)
13337 MOZ_ASSERT(IsInnerWindow());
13338 nsRefPtr<Gamepad> gamepad;
13339 if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
13340 return gamepad.forget();
13343 return nullptr;
13346 void
13347 nsGlobalWindow::SetHasSeenGamepadInput(bool aHasSeen)
13349 MOZ_ASSERT(IsInnerWindow());
13350 mHasSeenGamepadInput = aHasSeen;
13353 bool
13354 nsGlobalWindow::HasSeenGamepadInput()
13356 MOZ_ASSERT(IsInnerWindow());
13357 return mHasSeenGamepadInput;
13360 // static
13361 PLDHashOperator
13362 nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, Gamepad* aData,
13363 void* aUserArg)
13365 nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
13366 gamepadsvc->SyncGamepadState(aKey, aData);
13367 return PL_DHASH_NEXT;
13370 void
13371 nsGlobalWindow::SyncGamepadState()
13373 MOZ_ASSERT(IsInnerWindow());
13374 if (mHasSeenGamepadInput) {
13375 mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr);
13378 #endif
13379 // nsGlobalChromeWindow implementation
13381 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
13383 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
13384 nsGlobalWindow)
13385 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
13386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
13387 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroupMessageManagers)
13388 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
13391 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow,
13392 nsGlobalWindow)
13393 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserDOMWindow)
13394 if (tmp->mMessageManager) {
13395 static_cast<nsFrameMessageManager*>(
13396 tmp->mMessageManager.get())->Disconnect();
13397 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
13400 tmp->mGroupMessageManagers.EnumerateRead(DisconnectGroupMessageManager, nullptr);
13401 tmp->mGroupMessageManagers.Clear();
13402 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroupMessageManagers)
13403 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
13405 DOMCI_DATA(ChromeWindow, nsGlobalChromeWindow)
13407 // QueryInterface implementation for nsGlobalChromeWindow
13408 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalChromeWindow)
13409 NS_INTERFACE_MAP_ENTRY(nsIDOMChromeWindow)
13410 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ChromeWindow)
13411 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
13413 NS_IMPL_ADDREF_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
13414 NS_IMPL_RELEASE_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
13416 NS_IMETHODIMP
13417 nsGlobalChromeWindow::GetWindowState(uint16_t* aWindowState)
13419 *aWindowState = WindowState();
13420 return NS_OK;
13423 uint16_t
13424 nsGlobalWindow::WindowState()
13426 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13428 int32_t mode = widget ? widget->SizeMode() : 0;
13430 switch (mode) {
13431 case nsSizeMode_Minimized:
13432 return nsIDOMChromeWindow::STATE_MINIMIZED;
13433 case nsSizeMode_Maximized:
13434 return nsIDOMChromeWindow::STATE_MAXIMIZED;
13435 case nsSizeMode_Fullscreen:
13436 return nsIDOMChromeWindow::STATE_FULLSCREEN;
13437 case nsSizeMode_Normal:
13438 return nsIDOMChromeWindow::STATE_NORMAL;
13439 default:
13440 NS_WARNING("Illegal window state for this chrome window");
13441 break;
13444 return nsIDOMChromeWindow::STATE_NORMAL;
13447 NS_IMETHODIMP
13448 nsGlobalChromeWindow::Maximize()
13450 ErrorResult rv;
13451 Maximize(rv);
13452 return rv.ErrorCode();
13455 void
13456 nsGlobalWindow::Maximize(ErrorResult& aError)
13458 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13460 if (widget) {
13461 aError = widget->SetSizeMode(nsSizeMode_Maximized);
13465 NS_IMETHODIMP
13466 nsGlobalChromeWindow::Minimize()
13468 ErrorResult rv;
13469 Minimize(rv);
13470 return rv.ErrorCode();
13473 void
13474 nsGlobalWindow::Minimize(ErrorResult& aError)
13476 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13478 if (widget) {
13479 aError = widget->SetSizeMode(nsSizeMode_Minimized);
13483 NS_IMETHODIMP
13484 nsGlobalChromeWindow::Restore()
13486 ErrorResult rv;
13487 Restore(rv);
13488 return rv.ErrorCode();
13491 void
13492 nsGlobalWindow::Restore(ErrorResult& aError)
13494 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13496 if (widget) {
13497 aError = widget->SetSizeMode(nsSizeMode_Normal);
13501 NS_IMETHODIMP
13502 nsGlobalChromeWindow::GetAttention()
13504 ErrorResult rv;
13505 GetAttention(rv);
13506 return rv.ErrorCode();
13509 void
13510 nsGlobalWindow::GetAttention(ErrorResult& aResult)
13512 return GetAttentionWithCycleCount(-1, aResult);
13515 NS_IMETHODIMP
13516 nsGlobalChromeWindow::GetAttentionWithCycleCount(int32_t aCycleCount)
13518 ErrorResult rv;
13519 GetAttentionWithCycleCount(aCycleCount, rv);
13520 return rv.ErrorCode();
13523 void
13524 nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount,
13525 ErrorResult& aError)
13527 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13529 if (widget) {
13530 aError = widget->GetAttention(aCycleCount);
13534 NS_IMETHODIMP
13535 nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent, nsIDOMElement* aPanel)
13537 NS_ENSURE_TRUE(aMouseDownEvent, NS_ERROR_FAILURE);
13538 Event* mouseDownEvent = aMouseDownEvent->InternalDOMEvent();
13539 NS_ENSURE_TRUE(mouseDownEvent, NS_ERROR_FAILURE);
13541 nsCOMPtr<Element> panel = do_QueryInterface(aPanel);
13542 NS_ENSURE_TRUE(panel || !aPanel, NS_ERROR_FAILURE);
13544 ErrorResult rv;
13545 BeginWindowMove(*mouseDownEvent, panel, rv);
13546 return rv.ErrorCode();
13549 void
13550 nsGlobalWindow::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel,
13551 ErrorResult& aError)
13553 nsCOMPtr<nsIWidget> widget;
13555 // if a panel was supplied, use its widget instead.
13556 #ifdef MOZ_XUL
13557 if (aPanel) {
13558 nsIFrame* frame = aPanel->GetPrimaryFrame();
13559 if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame) {
13560 return;
13563 widget = (static_cast<nsMenuPopupFrame*>(frame))->GetWidget();
13565 else {
13566 #endif
13567 widget = GetMainWidget();
13568 #ifdef MOZ_XUL
13570 #endif
13572 if (!widget) {
13573 return;
13576 WidgetMouseEvent* mouseEvent =
13577 aMouseDownEvent.GetInternalNSEvent()->AsMouseEvent();
13578 if (!mouseEvent || mouseEvent->mClass != eMouseEventClass) {
13579 aError.Throw(NS_ERROR_FAILURE);
13580 return;
13583 aError = widget->BeginMoveDrag(mouseEvent);
13586 //Note: This call will lock the cursor, it will not change as it moves.
13587 //To unlock, the cursor must be set back to CURSOR_AUTO.
13588 NS_IMETHODIMP
13589 nsGlobalChromeWindow::SetCursor(const nsAString& aCursor)
13591 ErrorResult rv;
13592 SetCursor(aCursor, rv);
13593 return rv.ErrorCode();
13596 void
13597 nsGlobalWindow::SetCursor(const nsAString& aCursor, ErrorResult& aError)
13599 FORWARD_TO_OUTER_OR_THROW(SetCursor, (aCursor, aError), aError, );
13601 int32_t cursor;
13603 if (aCursor.EqualsLiteral("auto"))
13604 cursor = NS_STYLE_CURSOR_AUTO;
13605 else {
13606 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aCursor);
13607 if (eCSSKeyword_UNKNOWN == keyword ||
13608 !nsCSSProps::FindKeyword(keyword, nsCSSProps::kCursorKTable, cursor)) {
13609 return;
13613 nsRefPtr<nsPresContext> presContext;
13614 if (mDocShell) {
13615 mDocShell->GetPresContext(getter_AddRefs(presContext));
13618 if (presContext) {
13619 // Need root widget.
13620 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
13621 if (!presShell) {
13622 aError.Throw(NS_ERROR_FAILURE);
13623 return;
13626 nsViewManager* vm = presShell->GetViewManager();
13627 if (!vm) {
13628 aError.Throw(NS_ERROR_FAILURE);
13629 return;
13632 nsView* rootView = vm->GetRootView();
13633 if (!rootView) {
13634 aError.Throw(NS_ERROR_FAILURE);
13635 return;
13638 nsIWidget* widget = rootView->GetNearestWidget(nullptr);
13639 if (!widget) {
13640 aError.Throw(NS_ERROR_FAILURE);
13641 return;
13644 // Call esm and set cursor.
13645 aError = presContext->EventStateManager()->SetCursor(cursor, nullptr,
13646 false, 0.0f, 0.0f,
13647 widget, true);
13651 NS_IMETHODIMP
13652 nsGlobalChromeWindow::GetBrowserDOMWindow(nsIBrowserDOMWindow **aBrowserWindow)
13654 ErrorResult rv;
13655 NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv));
13656 return rv.ErrorCode();
13659 nsIBrowserDOMWindow*
13660 nsGlobalWindow::GetBrowserDOMWindow(ErrorResult& aError)
13662 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (aError), aError, nullptr);
13664 MOZ_ASSERT(IsChromeWindow());
13665 return static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow;
13668 NS_IMETHODIMP
13669 nsGlobalChromeWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow *aBrowserWindow)
13671 ErrorResult rv;
13672 SetBrowserDOMWindow(aBrowserWindow, rv);
13673 return rv.ErrorCode();
13676 void
13677 nsGlobalWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow,
13678 ErrorResult& aError)
13680 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindow, (aBrowserWindow, aError),
13681 aError, );
13682 MOZ_ASSERT(IsChromeWindow());
13683 static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow = aBrowserWindow;
13686 NS_IMETHODIMP
13687 nsGlobalChromeWindow::NotifyDefaultButtonLoaded(nsIDOMElement* aDefaultButton)
13689 nsCOMPtr<Element> defaultButton = do_QueryInterface(aDefaultButton);
13690 NS_ENSURE_ARG(defaultButton);
13692 ErrorResult rv;
13693 NotifyDefaultButtonLoaded(*defaultButton, rv);
13694 return rv.ErrorCode();
13697 void
13698 nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton,
13699 ErrorResult& aError)
13701 #ifdef MOZ_XUL
13702 // Don't snap to a disabled button.
13703 nsCOMPtr<nsIDOMXULControlElement> xulControl =
13704 do_QueryInterface(&aDefaultButton);
13705 if (!xulControl) {
13706 aError.Throw(NS_ERROR_FAILURE);
13707 return;
13709 bool disabled;
13710 aError = xulControl->GetDisabled(&disabled);
13711 if (aError.Failed() || disabled) {
13712 return;
13715 // Get the button rect in screen coordinates.
13716 nsIFrame *frame = aDefaultButton.GetPrimaryFrame();
13717 if (!frame) {
13718 aError.Throw(NS_ERROR_FAILURE);
13719 return;
13721 nsIntRect buttonRect = frame->GetScreenRect();
13723 // Get the widget rect in screen coordinates.
13724 nsIWidget *widget = GetNearestWidget();
13725 if (!widget) {
13726 aError.Throw(NS_ERROR_FAILURE);
13727 return;
13729 nsIntRect widgetRect;
13730 aError = widget->GetScreenBounds(widgetRect);
13731 if (aError.Failed()) {
13732 return;
13735 // Convert the buttonRect coordinates from screen to the widget.
13736 buttonRect -= widgetRect.TopLeft();
13737 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
13738 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
13739 aError.Throw(rv);
13741 #else
13742 aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
13743 #endif
13746 NS_IMETHODIMP
13747 nsGlobalChromeWindow::GetMessageManager(nsIMessageBroadcaster** aManager)
13749 ErrorResult rv;
13750 NS_IF_ADDREF(*aManager = GetMessageManager(rv));
13751 return rv.ErrorCode();
13754 nsIMessageBroadcaster*
13755 nsGlobalWindow::GetMessageManager(ErrorResult& aError)
13757 FORWARD_TO_INNER_OR_THROW(GetMessageManager, (aError), aError, nullptr);
13758 MOZ_ASSERT(IsChromeWindow());
13759 nsGlobalChromeWindow* myself = static_cast<nsGlobalChromeWindow*>(this);
13760 if (!myself->mMessageManager) {
13761 nsCOMPtr<nsIMessageBroadcaster> globalMM =
13762 do_GetService("@mozilla.org/globalmessagemanager;1");
13763 myself->mMessageManager =
13764 new nsFrameMessageManager(nullptr,
13765 static_cast<nsFrameMessageManager*>(globalMM.get()),
13766 MM_CHROME | MM_BROADCASTER);
13768 return myself->mMessageManager;
13771 NS_IMETHODIMP
13772 nsGlobalChromeWindow::GetGroupMessageManager(const nsAString& aGroup,
13773 nsIMessageBroadcaster** aManager)
13775 ErrorResult rv;
13776 NS_IF_ADDREF(*aManager = GetGroupMessageManager(aGroup, rv));
13777 return rv.ErrorCode();
13780 nsIMessageBroadcaster*
13781 nsGlobalWindow::GetGroupMessageManager(const nsAString& aGroup,
13782 ErrorResult& aError)
13784 FORWARD_TO_INNER_OR_THROW(GetGroupMessageManager, (aGroup, aError), aError, nullptr);
13785 MOZ_ASSERT(IsChromeWindow());
13787 nsGlobalChromeWindow* myself = static_cast<nsGlobalChromeWindow*>(this);
13788 nsCOMPtr<nsIMessageBroadcaster> messageManager =
13789 myself->mGroupMessageManagers.Get(aGroup);
13791 if (!messageManager) {
13792 nsFrameMessageManager* parent =
13793 static_cast<nsFrameMessageManager*>(GetMessageManager(aError));
13795 messageManager = new nsFrameMessageManager(nullptr,
13796 parent,
13797 MM_CHROME | MM_BROADCASTER);
13798 myself->mGroupMessageManagers.Put(aGroup, messageManager);
13801 return messageManager;
13804 // nsGlobalModalWindow implementation
13806 // QueryInterface implementation for nsGlobalModalWindow
13807 DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow)
13809 NS_INTERFACE_MAP_BEGIN(nsGlobalModalWindow)
13810 NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow)
13811 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow)
13812 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
13814 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
13815 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
13818 void
13819 nsGlobalWindow::GetDialogArguments(JSContext* aCx,
13820 JS::MutableHandle<JS::Value> aRetval,
13821 ErrorResult& aError)
13823 FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aRetval, aError),
13824 aError, );
13826 MOZ_ASSERT(IsModalContentWindow(),
13827 "This should only be called on modal windows!");
13829 if (!mDialogArguments) {
13830 MOZ_ASSERT(mIsClosed, "This window should be closed!");
13831 aRetval.setUndefined();
13832 return;
13835 // This does an internal origin check, and returns undefined if the subject
13836 // does not subsumes the origin of the arguments.
13837 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
13838 JSAutoCompartment ac(aCx, wrapper);
13839 mDialogArguments->Get(aCx, wrapper, nsContentUtils::SubjectPrincipal(),
13840 aRetval, aError);
13843 NS_IMETHODIMP
13844 nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
13846 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
13847 NS_ERROR_NOT_INITIALIZED);
13849 // This does an internal origin check, and returns undefined if the subject
13850 // does not subsumes the origin of the arguments.
13851 return mDialogArguments->Get(nsContentUtils::SubjectPrincipal(), aArguments);
13854 void
13855 nsGlobalWindow::GetReturnValue(JSContext* aCx,
13856 JS::MutableHandle<JS::Value> aReturnValue,
13857 ErrorResult& aError)
13859 FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aReturnValue, aError),
13860 aError, );
13862 MOZ_ASSERT(IsModalContentWindow(),
13863 "This should only be called on modal windows!");
13865 if (mReturnValue) {
13866 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
13867 JSAutoCompartment ac(aCx, wrapper);
13868 mReturnValue->Get(aCx, wrapper, nsContentUtils::SubjectPrincipal(),
13869 aReturnValue, aError);
13870 } else {
13871 aReturnValue.setUndefined();
13875 NS_IMETHODIMP
13876 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
13878 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
13880 nsCOMPtr<nsIVariant> result;
13881 if (!mReturnValue) {
13882 nsCOMPtr<nsIVariant> variant = CreateVoidVariant();
13883 variant.forget(aRetVal);
13884 return NS_OK;
13886 return mReturnValue->Get(nsContentUtils::SubjectPrincipal(), aRetVal);
13889 void
13890 nsGlobalWindow::SetReturnValue(JSContext* aCx,
13891 JS::Handle<JS::Value> aReturnValue,
13892 ErrorResult& aError)
13894 FORWARD_TO_OUTER_OR_THROW(SetReturnValue, (aCx, aReturnValue, aError),
13895 aError, );
13897 MOZ_ASSERT(IsModalContentWindow(),
13898 "This should only be called on modal windows!");
13900 nsCOMPtr<nsIVariant> returnValue;
13901 aError =
13902 nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue,
13903 getter_AddRefs(returnValue));
13904 if (!aError.Failed()) {
13905 mReturnValue = new DialogValueHolder(nsContentUtils::SubjectPrincipal(),
13906 returnValue);
13910 NS_IMETHODIMP
13911 nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal)
13913 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK);
13915 mReturnValue = new DialogValueHolder(nsContentUtils::SubjectPrincipal(),
13916 aRetVal);
13917 return NS_OK;
13920 /* static */
13921 bool
13922 nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal)
13924 // For now, have to deal with XPConnect objects here.
13925 return xpc::WindowOrNull(aGlobal)->IsModalContentWindow();
13928 NS_IMETHODIMP
13929 nsGlobalWindow::GetConsole(JSContext* aCx,
13930 JS::MutableHandle<JS::Value> aConsole)
13932 ErrorResult rv;
13933 nsRefPtr<Console> console = GetConsole(rv);
13934 if (rv.Failed()) {
13935 return rv.ErrorCode();
13938 if (!WrapNewBindingObject(aCx, console, aConsole)) {
13939 return NS_ERROR_FAILURE;
13942 return NS_OK;
13945 NS_IMETHODIMP
13946 nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
13948 JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
13949 if (!thisObj) {
13950 return NS_ERROR_UNEXPECTED;
13953 if (!JS_WrapObject(aCx, &thisObj) ||
13954 !JS_DefineProperty(aCx, thisObj, "console", aValue,
13955 JSPROP_ENUMERATE, JS_PropertyStub,
13956 JS_StrictPropertyStub)) {
13957 return NS_ERROR_FAILURE;
13960 return NS_OK;
13963 Console*
13964 nsGlobalWindow::GetConsole(ErrorResult& aRv)
13966 FORWARD_TO_INNER_OR_THROW(GetConsole, (aRv), aRv, nullptr);
13968 if (!mConsole) {
13969 mConsole = new Console(this);
13972 return mConsole;
13975 already_AddRefed<External>
13976 nsGlobalWindow::GetExternal(ErrorResult& aRv)
13978 FORWARD_TO_INNER_OR_THROW(GetExternal, (aRv), aRv, nullptr);
13980 #ifdef HAVE_SIDEBAR
13981 if (!mExternal) {
13982 AutoJSContext cx;
13983 JS::Rooted<JSObject*> jsImplObj(cx);
13984 ConstructJSImplementation(cx, "@mozilla.org/sidebar;1",
13985 this, &jsImplObj, aRv);
13986 if (aRv.Failed()) {
13987 return nullptr;
13989 mExternal = new External(jsImplObj, this);
13992 nsRefPtr<External> external = static_cast<External*>(mExternal.get());
13993 return external.forget();
13994 #else
13995 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
13996 return nullptr;
13997 #endif
14000 void
14001 nsGlobalWindow::GetSidebar(OwningExternalOrWindowProxy& aResult,
14002 ErrorResult& aRv)
14004 FORWARD_TO_INNER_OR_THROW(GetSidebar, (aResult, aRv), aRv, );
14006 #ifdef HAVE_SIDEBAR
14007 // First check for a named frame named "sidebar"
14008 nsCOMPtr<nsIDOMWindow> domWindow = GetChildWindow(NS_LITERAL_STRING("sidebar"));
14009 if (domWindow) {
14010 aResult.SetAsWindowProxy() = domWindow.forget();
14011 return;
14014 nsRefPtr<External> external = GetExternal(aRv);
14015 if (external) {
14016 aResult.SetAsExternal() = external;
14018 #else
14019 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
14020 #endif
14023 /* static */
14024 bool
14025 nsGlobalWindow::WindowOnWebIDL(JSContext* aCx, JSObject* aObj)
14027 DebugOnly<nsGlobalWindow*> win;
14028 MOZ_ASSERT_IF(IsDOMObject(aObj),
14029 NS_SUCCEEDED(UNWRAP_OBJECT(Window, aObj, win)));
14031 return IsDOMObject(aObj);
14034 #ifdef MOZ_B2G
14035 void
14036 nsGlobalWindow::EnableNetworkEvent(uint32_t aType)
14038 MOZ_ASSERT(IsInnerWindow());
14040 nsCOMPtr<nsIPermissionManager> permMgr =
14041 services::GetPermissionManager();
14042 if (!permMgr) {
14043 NS_ERROR("No PermissionManager available!");
14044 return;
14047 uint32_t permission = nsIPermissionManager::DENY_ACTION;
14048 permMgr->TestExactPermissionFromPrincipal(GetPrincipal(), "network-events",
14049 &permission);
14051 if (permission != nsIPermissionManager::ALLOW_ACTION) {
14052 return;
14055 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
14056 if (!os) {
14057 NS_ERROR("ObserverService should be available!");
14058 return;
14061 switch (aType) {
14062 case NS_NETWORK_UPLOAD_EVENT:
14063 if (!mNetworkUploadObserverEnabled) {
14064 mNetworkUploadObserverEnabled = true;
14065 os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC, false);
14067 break;
14068 case NS_NETWORK_DOWNLOAD_EVENT:
14069 if (!mNetworkDownloadObserverEnabled) {
14070 mNetworkDownloadObserverEnabled = true;
14071 os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, false);
14073 break;
14077 void
14078 nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
14080 MOZ_ASSERT(IsInnerWindow());
14082 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
14083 if (!os) {
14084 return;
14087 switch (aType) {
14088 case NS_NETWORK_UPLOAD_EVENT:
14089 if (mNetworkUploadObserverEnabled) {
14090 mNetworkUploadObserverEnabled = false;
14091 os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC);
14093 break;
14094 case NS_NETWORK_DOWNLOAD_EVENT:
14095 if (mNetworkDownloadObserverEnabled) {
14096 mNetworkDownloadObserverEnabled = false;
14097 os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC);
14099 break;
14102 #endif // MOZ_B2G
14104 #define EVENT(name_, id_, type_, struct_) \
14105 NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \
14106 JS::MutableHandle<JS::Value> vp) { \
14107 EventHandlerNonNull* h = GetOn##name_(); \
14108 vp.setObjectOrNull(h ? h->Callable().get() : nullptr); \
14109 return NS_OK; \
14111 NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \
14112 JS::Handle<JS::Value> v) { \
14113 nsRefPtr<EventHandlerNonNull> handler; \
14114 JS::Rooted<JSObject*> callable(cx); \
14115 if (v.isObject() && \
14116 JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
14117 handler = new EventHandlerNonNull(callable, GetIncumbentGlobal()); \
14119 SetOn##name_(handler); \
14120 return NS_OK; \
14122 #define ERROR_EVENT(name_, id_, type_, struct_) \
14123 NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \
14124 JS::MutableHandle<JS::Value> vp) { \
14125 EventListenerManager *elm = GetExistingListenerManager(); \
14126 if (elm) { \
14127 OnErrorEventHandlerNonNull* h = elm->GetOnErrorEventHandler(); \
14128 if (h) { \
14129 vp.setObject(*h->Callable()); \
14130 return NS_OK; \
14133 vp.setNull(); \
14134 return NS_OK; \
14136 NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \
14137 JS::Handle<JS::Value> v) { \
14138 EventListenerManager *elm = GetOrCreateListenerManager(); \
14139 if (!elm) { \
14140 return NS_ERROR_OUT_OF_MEMORY; \
14143 nsRefPtr<OnErrorEventHandlerNonNull> handler; \
14144 JS::Rooted<JSObject*> callable(cx); \
14145 if (v.isObject() && \
14146 JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
14147 handler = new OnErrorEventHandlerNonNull(callable, GetIncumbentGlobal()); \
14149 elm->SetEventHandler(handler); \
14150 return NS_OK; \
14152 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
14153 NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \
14154 JS::MutableHandle<JS::Value> vp) { \
14155 EventListenerManager *elm = GetExistingListenerManager(); \
14156 if (elm) { \
14157 OnBeforeUnloadEventHandlerNonNull* h = \
14158 elm->GetOnBeforeUnloadEventHandler(); \
14159 if (h) { \
14160 vp.setObject(*h->Callable()); \
14161 return NS_OK; \
14164 vp.setNull(); \
14165 return NS_OK; \
14167 NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \
14168 JS::Handle<JS::Value> v) { \
14169 EventListenerManager *elm = GetOrCreateListenerManager(); \
14170 if (!elm) { \
14171 return NS_ERROR_OUT_OF_MEMORY; \
14174 nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler; \
14175 JS::Rooted<JSObject*> callable(cx); \
14176 if (v.isObject() && \
14177 JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
14178 handler = new OnBeforeUnloadEventHandlerNonNull(callable, GetIncumbentGlobal()); \
14180 elm->SetEventHandler(handler); \
14181 return NS_OK; \
14183 #define WINDOW_ONLY_EVENT EVENT
14184 #define TOUCH_EVENT EVENT
14185 #include "mozilla/EventNameList.h"
14186 #undef TOUCH_EVENT
14187 #undef WINDOW_ONLY_EVENT
14188 #undef BEFOREUNLOAD_EVENT
14189 #undef ERROR_EVENT
14190 #undef EVENT
14192 #ifdef _WINDOWS_
14193 #error "Never include windows.h in this file!"
14194 #endif