Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 "nsIDOMStorage.h"
20 #include "nsIDOMStorageManager.h"
21 #include "DOMStorage.h"
22 #include "nsDOMOfflineResourceList.h"
23 #include "nsError.h"
24 #include "nsIIdleService.h"
25 #include "nsISizeOfEventTarget.h"
26 #include "nsDOMJSUtils.h"
27 #include "nsArrayUtils.h"
28 #include "nsIDOMWindowCollection.h"
29 #include "nsDOMWindowList.h"
30 #include "mozilla/dom/WakeLock.h"
31 #include "mozilla/dom/power/PowerManagerService.h"
32 #include "nsIDocShellTreeOwner.h"
33 #include "nsIPermissionManager.h"
34 #include "nsIScriptContext.h"
35 #include "nsIScriptTimeoutHandler.h"
36 #include "nsIController.h"
37 #include "nsScriptNameSpaceManager.h"
38 #include "nsWindowMemoryReporter.h"
40 // Helper Classes
41 #include "nsJSUtils.h"
42 #include "jsapi.h" // for JSAutoRequest
43 #include "js/OldDebugAPI.h" // for JS_ClearWatchPointsForObject
44 #include "jswrapper.h"
45 #include "nsReadableUtils.h"
46 #include "nsDOMClassInfo.h"
47 #include "nsJSEnvironment.h"
48 #include "ScriptSettings.h"
49 #include "mozilla/Preferences.h"
50 #include "mozilla/Likely.h"
51 #include "mozilla/unused.h"
53 // Other Classes
54 #include "mozilla/dom/BarProps.h"
55 #include "nsContentCID.h"
56 #include "nsLayoutStatics.h"
57 #include "nsCCUncollectableMarker.h"
58 #include "mozilla/dom/workers/Workers.h"
59 #include "mozilla/dom/MessagePortList.h"
60 #include "nsJSPrincipals.h"
61 #include "mozilla/Attributes.h"
62 #include "mozilla/Debug.h"
63 #include "mozilla/EventListenerManager.h"
64 #include "mozilla/EventStates.h"
65 #include "mozilla/MouseEvents.h"
66 #include "AudioChannelService.h"
67 #include "MessageEvent.h"
69 // Interfaces Needed
70 #include "nsIFrame.h"
71 #include "nsCanvasFrame.h"
72 #include "nsIWidget.h"
73 #include "nsIWidgetListener.h"
74 #include "nsIBaseWindow.h"
75 #include "nsIDeviceSensors.h"
76 #include "nsIContent.h"
77 #include "nsIDocShell.h"
78 #include "nsIDocCharset.h"
79 #include "nsIDocument.h"
80 #include "Crypto.h"
81 #ifndef MOZ_DISABLE_CRYPTOLEGACY
82 #include "nsIDOMCryptoLegacy.h"
83 #endif
84 #include "nsIDOMDocument.h"
85 #include "nsIDOMElement.h"
86 #include "nsIDOMEvent.h"
87 #include "nsIDOMPopupBlockedEvent.h"
88 #include "nsIDOMPopStateEvent.h"
89 #include "nsIDOMHashChangeEvent.h"
90 #include "nsIDOMOfflineResourceList.h"
91 #include "nsPIDOMStorage.h"
92 #include "nsDOMString.h"
93 #include "nsIEmbeddingSiteWindow.h"
94 #include "nsThreadUtils.h"
95 #include "nsILoadContext.h"
96 #include "nsIMarkupDocumentViewer.h"
97 #include "nsIPresShell.h"
98 #include "nsIScriptSecurityManager.h"
99 #include "nsIScrollableFrame.h"
100 #include "nsView.h"
101 #include "nsViewManager.h"
102 #include "nsISelectionController.h"
103 #include "nsISelection.h"
104 #include "nsIPrompt.h"
105 #include "nsIPromptService.h"
106 #include "nsIPromptFactory.h"
107 #include "nsIWritablePropertyBag2.h"
108 #include "nsIWebNavigation.h"
109 #include "nsIWebBrowserChrome.h"
110 #include "nsIWebBrowserFind.h" // For window.find()
111 #include "nsIWindowMediator.h" // For window.find()
112 #include "nsComputedDOMStyle.h"
113 #include "nsIEntropyCollector.h"
114 #include "nsDOMCID.h"
115 #include "nsDOMWindowUtils.h"
116 #include "nsIWindowWatcher.h"
117 #include "nsPIWindowWatcher.h"
118 #include "nsIContentViewer.h"
119 #include "nsIScriptError.h"
120 #include "nsIControllers.h"
121 #include "nsIControllerContext.h"
122 #include "nsGlobalWindowCommands.h"
123 #include "nsAutoPtr.h"
124 #include "nsContentUtils.h"
125 #include "nsCxPusher.h"
126 #include "nsCSSProps.h"
127 #include "nsIDOMFile.h"
128 #include "nsIDOMFileList.h"
129 #include "nsIURIFixup.h"
130 #ifndef DEBUG
131 #include "nsIAppStartup.h"
132 #include "nsToolkitCompsCID.h"
133 #endif
134 #include "nsCDefaultURIFixup.h"
135 #include "mozilla/EventDispatcher.h"
136 #include "mozilla/EventStateManager.h"
137 #include "nsIObserverService.h"
138 #include "nsFocusManager.h"
139 #include "nsIXULWindow.h"
140 #include "nsITimedChannel.h"
141 #include "nsServiceManagerUtils.h"
142 #ifdef MOZ_XUL
143 #include "nsIDOMXULControlElement.h"
144 #include "nsMenuPopupFrame.h"
145 #endif
146 #include "nsIDOMCustomEvent.h"
147 #include "nsIFrameRequestCallback.h"
148 #include "nsIJARChannel.h"
150 #include "xpcprivate.h"
152 #ifdef NS_PRINTING
153 #include "nsIPrintSettings.h"
154 #include "nsIPrintSettingsService.h"
155 #include "nsIWebBrowserPrint.h"
156 #endif
158 #include "nsWindowRoot.h"
159 #include "nsNetCID.h"
160 #include "nsIArray.h"
162 // XXX An unfortunate dependency exists here (two XUL files).
163 #include "nsIDOMXULDocument.h"
164 #include "nsIDOMXULCommandDispatcher.h"
166 #include "nsBindingManager.h"
167 #include "nsXBLService.h"
169 // used for popup blocking, needs to be converted to something
170 // belonging to the back-end like nsIContentPolicy
171 #include "nsIPopupWindowManager.h"
173 #include "nsIDragService.h"
174 #include "mozilla/dom/Element.h"
175 #include "mozilla/dom/Selection.h"
176 #include "nsFrameLoader.h"
177 #include "nsISupportsPrimitives.h"
178 #include "nsXPCOMCID.h"
179 #include "GeneratedEvents.h"
180 #include "GeneratedEventClasses.h"
181 #include "mozIThirdPartyUtil.h"
182 #ifdef MOZ_LOGGING
183 // so we can get logging even in release builds
184 #define FORCE_PR_LOG 1
185 #endif
186 #include "prlog.h"
187 #include "prenv.h"
188 #include "prprf.h"
190 #include "mozilla/dom/MessageChannel.h"
191 #include "mozilla/dom/MessagePort.h"
192 #include "mozilla/dom/MessagePortBinding.h"
193 #include "mozilla/dom/indexedDB/IDBFactory.h"
194 #include "mozilla/dom/quota/QuotaManager.h"
196 #include "mozilla/dom/StructuredCloneTags.h"
198 #ifdef MOZ_GAMEPAD
199 #include "mozilla/dom/GamepadService.h"
200 #endif
202 #include "nsRefreshDriver.h"
204 #include "mozilla/Services.h"
205 #include "mozilla/Telemetry.h"
206 #include "nsLocation.h"
207 #include "nsHTMLDocument.h"
208 #include "nsWrapperCacheInlines.h"
209 #include "mozilla/DOMEventTargetHelper.h"
210 #include "prrng.h"
211 #include "nsSandboxFlags.h"
212 #include "TimeChangeObserver.h"
213 #include "mozilla/dom/AudioContext.h"
214 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
215 #include "mozilla/dom/Console.h"
216 #include "mozilla/dom/FunctionBinding.h"
217 #include "mozilla/dom/WindowBinding.h"
218 #include "nsITabChild.h"
219 #include "mozilla/dom/MediaQueryList.h"
220 #include "mozilla/dom/ScriptSettings.h"
221 #ifdef HAVE_SIDEBAR
222 #include "mozilla/dom/ExternalBinding.h"
223 #endif
225 #ifdef MOZ_WEBSPEECH
226 #include "mozilla/dom/SpeechSynthesis.h"
227 #endif
229 #ifdef MOZ_JSDEBUGGER
230 #include "jsdIDebuggerService.h"
231 #endif
233 #ifdef MOZ_B2G
234 #include "nsPISocketTransportService.h"
235 #endif
237 // Apple system headers seem to have a check() macro. <sigh>
238 #ifdef check
239 class nsIScriptTimeoutHandler;
240 #undef check
241 #endif // check
242 #include "AccessCheck.h"
244 #ifdef ANDROID
245 #include <android/log.h>
246 #endif
248 #ifdef PR_LOGGING
249 static PRLogModuleInfo* gDOMLeakPRLog;
250 #endif
252 #ifdef XP_WIN
253 #include <process.h>
254 #define getpid _getpid
255 #else
256 #include <unistd.h> // for getpid()
257 #endif
259 static const char kStorageEnabled[] = "dom.storage.enabled";
261 using namespace mozilla;
262 using namespace mozilla::dom;
263 using namespace mozilla::dom::ipc;
264 using mozilla::TimeStamp;
265 using mozilla::TimeDuration;
267 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
268 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
269 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
271 static nsIEntropyCollector *gEntropyCollector = nullptr;
272 static int32_t gRefCnt = 0;
273 static int32_t gOpenPopupSpamCount = 0;
274 static PopupControlState gPopupControlState = openAbused;
275 static int32_t gRunningTimeoutDepth = 0;
276 static bool gMouseDown = false;
277 static bool gDragServiceDisabled = false;
278 static FILE *gDumpFile = nullptr;
279 static uint64_t gNextWindowID = 0;
280 static uint32_t gSerialCounter = 0;
281 static uint32_t gTimeoutsRecentlySet = 0;
282 static TimeStamp gLastRecordedRecentTimeouts;
283 #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
285 #ifdef DEBUG_jst
286 int32_t gTimeoutCnt = 0;
287 #endif
289 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
290 #define DEBUG_PAGE_CACHE
291 #endif
293 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
295 // The default shortest interval/timeout we permit
296 #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
297 #define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
298 static int32_t gMinTimeoutValue;
299 static int32_t gMinBackgroundTimeoutValue;
300 inline int32_t
301 nsGlobalWindow::DOMMinTimeoutValue() const {
302 bool isBackground = !mOuterWindow || mOuterWindow->IsBackground();
303 return
304 std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, 0);
305 }
307 // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
308 // uses 5.
309 #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
311 // The longest interval (as PRIntervalTime) we permit, or that our
312 // timer code can handle, really. See DELAY_INTERVAL_LIMIT in
313 // nsTimerImpl.h for details.
314 #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT
316 #define FORWARD_TO_OUTER(method, args, err_rval) \
317 PR_BEGIN_MACRO \
318 if (IsInnerWindow()) { \
319 nsGlobalWindow *outer = GetOuterWindowInternal(); \
320 if (!HasActiveDocument()) { \
321 NS_WARNING(outer ? \
322 "Inner window does not have active document." : \
323 "No outer window available!"); \
324 return err_rval; \
325 } \
326 return outer->method args; \
327 } \
328 PR_END_MACRO
330 #define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \
331 PR_BEGIN_MACRO \
332 if (IsInnerWindow()) { \
333 nsGlobalWindow *outer = GetOuterWindowInternal(); \
334 if (!HasActiveDocument()) { \
335 if (!outer) { \
336 NS_WARNING("No outer window available!"); \
337 errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \
338 } else { \
339 errorresult.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \
340 } \
341 } else { \
342 return outer->method args; \
343 } \
344 return err_rval; \
345 } \
346 PR_END_MACRO
348 #define FORWARD_TO_OUTER_VOID(method, args) \
349 PR_BEGIN_MACRO \
350 if (IsInnerWindow()) { \
351 nsGlobalWindow *outer = GetOuterWindowInternal(); \
352 if (!HasActiveDocument()) { \
353 NS_WARNING(outer ? \
354 "Inner window does not have active document." : \
355 "No outer window available!"); \
356 return; \
357 } \
358 outer->method args; \
359 return; \
360 } \
361 PR_END_MACRO
363 #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \
364 PR_BEGIN_MACRO \
365 if (IsInnerWindow()) { \
366 nsGlobalWindow *outer = GetOuterWindowInternal(); \
367 if (!HasActiveDocument()) { \
368 NS_WARNING(outer ? \
369 "Inner window does not have active document." : \
370 "No outer window available!"); \
371 return err_rval; \
372 } \
373 return ((nsGlobalChromeWindow *)outer)->method args; \
374 } \
375 PR_END_MACRO
377 #define FORWARD_TO_INNER_CHROME(method, args, err_rval) \
378 PR_BEGIN_MACRO \
379 if (IsOuterWindow()) { \
380 if (!mInnerWindow) { \
381 NS_WARNING("No inner window available!"); \
382 return err_rval; \
383 } \
384 return ((nsGlobalChromeWindow *)mInnerWindow)->method args; \
385 } \
386 PR_END_MACRO
388 #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \
389 PR_BEGIN_MACRO \
390 if (IsInnerWindow()) { \
391 nsGlobalWindow *outer = GetOuterWindowInternal(); \
392 if (!HasActiveDocument()) { \
393 NS_WARNING(outer ? \
394 "Inner window does not have active document." : \
395 "No outer window available!"); \
396 return err_rval; \
397 } \
398 return ((nsGlobalModalWindow *)outer)->method args; \
399 } \
400 PR_END_MACRO
402 #define FORWARD_TO_INNER(method, args, err_rval) \
403 PR_BEGIN_MACRO \
404 if (IsOuterWindow()) { \
405 if (!mInnerWindow) { \
406 NS_WARNING("No inner window available!"); \
407 return err_rval; \
408 } \
409 return GetCurrentInnerWindowInternal()->method args; \
410 } \
411 PR_END_MACRO
413 #define FORWARD_TO_INNER_OR_THROW(method, args, errorresult, err_rval) \
414 PR_BEGIN_MACRO \
415 if (IsOuterWindow()) { \
416 if (!mInnerWindow) { \
417 NS_WARNING("No inner window available!"); \
418 errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \
419 return err_rval; \
420 } \
421 return GetCurrentInnerWindowInternal()->method args; \
422 } \
423 PR_END_MACRO
425 #define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \
426 PR_BEGIN_MACRO \
427 if (IsOuterWindow()) { \
428 if (!mInnerWindow) { \
429 NS_WARNING("No inner window available!"); \
430 return err_rval; \
431 } \
432 return ((nsGlobalModalWindow*)GetCurrentInnerWindowInternal())->method args; \
433 } \
434 PR_END_MACRO
436 #define FORWARD_TO_INNER_VOID(method, args) \
437 PR_BEGIN_MACRO \
438 if (IsOuterWindow()) { \
439 if (!mInnerWindow) { \
440 NS_WARNING("No inner window available!"); \
441 return; \
442 } \
443 GetCurrentInnerWindowInternal()->method args; \
444 return; \
445 } \
446 PR_END_MACRO
448 // Same as FORWARD_TO_INNER, but this will create a fresh inner if an
449 // inner doesn't already exists.
450 #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \
451 PR_BEGIN_MACRO \
452 if (IsOuterWindow()) { \
453 if (!mInnerWindow) { \
454 if (mIsClosed) { \
455 return err_rval; \
456 } \
457 nsCOMPtr<nsIDOMDocument> doc; \
458 nsresult fwdic_nr = GetDocument(getter_AddRefs(doc)); \
459 NS_ENSURE_SUCCESS(fwdic_nr, err_rval); \
460 if (!mInnerWindow) { \
461 return err_rval; \
462 } \
463 } \
464 return GetCurrentInnerWindowInternal()->method args; \
465 } \
466 PR_END_MACRO
468 // CIDs
469 static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
471 static const char sPopStatePrefStr[] = "browser.history.allowPopState";
473 #define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload")
474 #define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload")
476 /**
477 * An indirect observer object that means we don't have to implement nsIObserver
478 * on nsGlobalWindow, where any script could see it.
479 */
480 class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver,
481 public nsIInterfaceRequestor
482 {
483 public:
484 nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {}
485 NS_DECL_ISUPPORTS
486 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
487 {
488 if (!mWindow)
489 return NS_OK;
490 return mWindow->Observe(aSubject, aTopic, aData);
491 }
492 void Forget() { mWindow = nullptr; }
493 NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult)
494 {
495 if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) {
496 return mWindow->QueryInterface(aIID, aResult);
497 }
498 return NS_NOINTERFACE;
499 }
501 private:
502 nsGlobalWindow* mWindow;
503 };
505 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
507 nsTimeout::nsTimeout()
508 : mCleared(false),
509 mRunning(false),
510 mIsInterval(false),
511 mPublicId(0),
512 mInterval(0),
513 mFiringDepth(0),
514 mNestingLevel(0),
515 mPopupState(openAllowed)
516 {
517 #ifdef DEBUG_jst
518 {
519 extern int gTimeoutCnt;
521 ++gTimeoutCnt;
522 }
523 #endif
525 MOZ_COUNT_CTOR(nsTimeout);
526 }
528 nsTimeout::~nsTimeout()
529 {
530 #ifdef DEBUG_jst
531 {
532 extern int gTimeoutCnt;
534 --gTimeoutCnt;
535 }
536 #endif
538 if (mTimer) {
539 mTimer->Cancel();
540 mTimer = nullptr;
541 }
543 MOZ_COUNT_DTOR(nsTimeout);
544 }
546 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout)
548 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout)
549 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout)
550 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
551 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
552 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler)
553 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
554 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef)
555 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release)
557 // Return true if this timeout has a refcount of 1. This is used to check
558 // that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.
559 bool
560 nsTimeout::HasRefCntOne()
561 {
562 return mRefCnt.get() == 1;
563 }
565 nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
566 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
567 mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
568 mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
569 mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
570 mMayHaveMouseEnterLeaveEventListener(false),
571 mMayHavePointerEnterLeaveEventListener(false),
572 mIsModalContentWindow(false),
573 mIsActive(false), mIsBackground(false),
574 mAudioMuted(false), mAudioVolume(1.0),
575 mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
576 // Make sure no actual window ends up with mWindowID == 0
577 mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
578 mMarkedCCGeneration(0)
579 {}
581 nsPIDOMWindow::~nsPIDOMWindow() {}
583 // DialogValueHolder CC goop.
584 NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue)
586 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
587 NS_INTERFACE_MAP_ENTRY(nsISupports)
588 NS_INTERFACE_MAP_END
590 NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder)
591 NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder)
593 //*****************************************************************************
594 // nsOuterWindowProxy: Outer Window Proxy
595 //*****************************************************************************
597 class nsOuterWindowProxy : public js::Wrapper
598 {
599 public:
600 nsOuterWindowProxy() : js::Wrapper(0) { }
602 virtual bool finalizeInBackground(JS::Value priv) {
603 return false;
604 }
606 virtual const char *className(JSContext *cx,
607 JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
608 virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
610 // Fundamental traps
611 virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
612 MOZ_OVERRIDE;
613 virtual bool preventExtensions(JSContext *cx,
614 JS::Handle<JSObject*> proxy) MOZ_OVERRIDE;
615 virtual bool getPropertyDescriptor(JSContext* cx,
616 JS::Handle<JSObject*> proxy,
617 JS::Handle<jsid> id,
618 JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
619 virtual bool getOwnPropertyDescriptor(JSContext* cx,
620 JS::Handle<JSObject*> proxy,
621 JS::Handle<jsid> id,
622 JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
623 virtual bool defineProperty(JSContext* cx,
624 JS::Handle<JSObject*> proxy,
625 JS::Handle<jsid> id,
626 JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
627 virtual bool getOwnPropertyNames(JSContext *cx,
628 JS::Handle<JSObject*> proxy,
629 JS::AutoIdVector &props) MOZ_OVERRIDE;
630 virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
631 JS::Handle<jsid> id,
632 bool *bp) MOZ_OVERRIDE;
633 virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
634 JS::AutoIdVector &props) MOZ_OVERRIDE;
636 virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy,
637 JS::Handle<jsid> id, JS::Handle<JSObject*> callable) MOZ_OVERRIDE;
638 virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
639 JS::Handle<jsid> id) MOZ_OVERRIDE;
641 // Derived traps
642 virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
643 JS::Handle<jsid> id, bool *bp) MOZ_OVERRIDE;
644 virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
645 JS::Handle<jsid> id, bool *bp) MOZ_OVERRIDE;
646 virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
647 JS::Handle<JSObject*> receiver,
648 JS::Handle<jsid> id,
649 JS::MutableHandle<JS::Value> vp) MOZ_OVERRIDE;
650 virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
651 JS::Handle<JSObject*> receiver,
652 JS::Handle<jsid> id,
653 bool strict,
654 JS::MutableHandle<JS::Value> vp) MOZ_OVERRIDE;
655 virtual bool keys(JSContext *cx, JS::Handle<JSObject*> proxy,
656 JS::AutoIdVector &props) MOZ_OVERRIDE;
657 virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
658 unsigned flags,
659 JS::MutableHandle<JS::Value> vp) MOZ_OVERRIDE;
661 static nsOuterWindowProxy singleton;
663 protected:
664 nsGlobalWindow* GetWindow(JSObject *proxy)
665 {
666 return nsGlobalWindow::FromSupports(
667 static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
668 }
670 // False return value means we threw an exception. True return value
671 // but false "found" means we didn't have a subframe at that index.
672 bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy,
673 JS::Handle<jsid> id,
674 JS::MutableHandle<JS::Value> vp,
675 bool &found);
677 // Returns a non-null window only if id is an index and we have a
678 // window at that index.
679 already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx,
680 JS::Handle<JSObject*> proxy,
681 JS::Handle<jsid> id);
683 bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
684 JS::AutoIdVector &props);
685 };
687 const js::Class OuterWindowProxyClass =
688 PROXY_CLASS_WITH_EXT(
689 "Proxy",
690 0, /* additional slots */
691 0, /* additional class flags */
692 nullptr, /* call */
693 nullptr, /* construct */
694 PROXY_MAKE_EXT(
695 nullptr, /* outerObject */
696 js::proxy_innerObject,
697 nullptr, /* iteratorObject */
698 false /* isWrappedNative */
699 ));
701 bool
702 nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
703 bool *extensible)
704 {
705 // If [[Extensible]] could be false, then navigating a window could navigate
706 // to a window that's [[Extensible]] after being at one that wasn't: an
707 // invariant violation. So always report true for this.
708 *extensible = true;
709 return true;
710 }
712 bool
713 nsOuterWindowProxy::preventExtensions(JSContext *cx,
714 JS::Handle<JSObject*> proxy)
715 {
716 // See above.
717 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
718 JSMSG_CANT_CHANGE_EXTENSIBILITY);
719 return false;
720 }
722 const char *
723 nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy)
724 {
725 MOZ_ASSERT(js::IsProxy(proxy));
727 return "Window";
728 }
730 void
731 nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy)
732 {
733 nsGlobalWindow* global = GetWindow(proxy);
734 if (global) {
735 global->ClearWrapper();
737 // Ideally we would use OnFinalize here, but it's possible that
738 // EnsureScriptEnvironment will later be called on the window, and we don't
739 // want to create a new script object in that case. Therefore, we need to
740 // write a non-null value that will reliably crash when dereferenced.
741 global->PoisonOuterWindowProxy(proxy);
742 }
743 }
745 bool
746 nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx,
747 JS::Handle<JSObject*> proxy,
748 JS::Handle<jsid> id,
749 JS::MutableHandle<JSPropertyDescriptor> desc)
750 {
751 // The only thing we can do differently from js::Wrapper is shadow stuff with
752 // our indexed properties, so we can just try getOwnPropertyDescriptor and if
753 // that gives us nothing call on through to js::Wrapper.
754 desc.object().set(nullptr);
755 if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) {
756 return false;
757 }
759 if (desc.object()) {
760 return true;
761 }
763 return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc);
764 }
766 bool
767 nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx,
768 JS::Handle<JSObject*> proxy,
769 JS::Handle<jsid> id,
770 JS::MutableHandle<JSPropertyDescriptor> desc)
771 {
772 bool found;
773 if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) {
774 return false;
775 }
776 if (found) {
777 FillPropertyDescriptor(desc, proxy, true);
778 return true;
779 }
780 // else fall through to js::Wrapper
782 return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
783 }
785 bool
786 nsOuterWindowProxy::defineProperty(JSContext* cx,
787 JS::Handle<JSObject*> proxy,
788 JS::Handle<jsid> id,
789 JS::MutableHandle<JSPropertyDescriptor> desc)
790 {
791 int32_t index = GetArrayIndexFromId(cx, id);
792 if (IsArrayIndex(index)) {
793 // Spec says to Reject whether this is a supported index or not,
794 // since we have no indexed setter or indexed creator. That means
795 // throwing in strict mode (FIXME: Bug 828137), doing nothing in
796 // non-strict mode.
797 return true;
798 }
800 return js::Wrapper::defineProperty(cx, proxy, id, desc);
801 }
803 bool
804 nsOuterWindowProxy::getOwnPropertyNames(JSContext *cx,
805 JS::Handle<JSObject*> proxy,
806 JS::AutoIdVector &props)
807 {
808 // Just our indexed stuff followed by our "normal" own property names.
809 if (!AppendIndexedPropertyNames(cx, proxy, props)) {
810 return false;
811 }
813 JS::AutoIdVector innerProps(cx);
814 if (!js::Wrapper::getOwnPropertyNames(cx, proxy, innerProps)) {
815 return false;
816 }
817 return js::AppendUnique(cx, props, innerProps);
818 }
820 bool
821 nsOuterWindowProxy::delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
822 JS::Handle<jsid> id, bool *bp)
823 {
824 if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
825 // Reject (which means throw if strict, else return false) the delete.
826 // Except we don't even know whether we're strict. See bug 803157.
827 *bp = false;
828 return true;
829 }
831 int32_t index = GetArrayIndexFromId(cx, id);
832 if (IsArrayIndex(index)) {
833 // Indexed, but not supported. Spec says return true.
834 *bp = true;
835 return true;
836 }
838 return js::Wrapper::delete_(cx, proxy, id, bp);
839 }
841 bool
842 nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
843 JS::AutoIdVector &props)
844 {
845 // Just our indexed stuff followed by our "normal" own property names.
846 if (!AppendIndexedPropertyNames(cx, proxy, props)) {
847 return false;
848 }
850 JS::AutoIdVector innerProps(cx);
851 if (!js::Wrapper::enumerate(cx, proxy, innerProps)) {
852 return false;
853 }
854 return js::AppendUnique(cx, props, innerProps);
855 }
857 bool
858 nsOuterWindowProxy::has(JSContext *cx, JS::Handle<JSObject*> proxy,
859 JS::Handle<jsid> id, bool *bp)
860 {
861 if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
862 *bp = true;
863 return true;
864 }
866 return js::Wrapper::has(cx, proxy, id, bp);
867 }
869 bool
870 nsOuterWindowProxy::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
871 JS::Handle<jsid> id, bool *bp)
872 {
873 if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
874 *bp = true;
875 return true;
876 }
878 return js::Wrapper::hasOwn(cx, proxy, id, bp);
879 }
881 bool
882 nsOuterWindowProxy::get(JSContext *cx, JS::Handle<JSObject*> proxy,
883 JS::Handle<JSObject*> receiver,
884 JS::Handle<jsid> id,
885 JS::MutableHandle<JS::Value> vp)
886 {
887 if (id == nsDOMClassInfo::sWrappedJSObject_id &&
888 xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
889 vp.set(JS::ObjectValue(*proxy));
890 return true;
891 }
893 bool found;
894 if (!GetSubframeWindow(cx, proxy, id, vp, found)) {
895 return false;
896 }
897 if (found) {
898 return true;
899 }
900 // Else fall through to js::Wrapper
902 return js::Wrapper::get(cx, proxy, receiver, id, vp);
903 }
905 bool
906 nsOuterWindowProxy::set(JSContext *cx, JS::Handle<JSObject*> proxy,
907 JS::Handle<JSObject*> receiver,
908 JS::Handle<jsid> id,
909 bool strict,
910 JS::MutableHandle<JS::Value> vp)
911 {
912 int32_t index = GetArrayIndexFromId(cx, id);
913 if (IsArrayIndex(index)) {
914 // Reject (which means throw if and only if strict) the set.
915 if (strict) {
916 // XXXbz This needs to throw, but see bug 828137.
917 }
918 return true;
919 }
921 return js::Wrapper::set(cx, proxy, receiver, id, strict, vp);
922 }
924 bool
925 nsOuterWindowProxy::keys(JSContext *cx, JS::Handle<JSObject*> proxy,
926 JS::AutoIdVector &props)
927 {
928 // BaseProxyHandler::keys seems to do what we want here: call
929 // getOwnPropertyNames and then filter out the non-enumerable properties.
930 return js::BaseProxyHandler::keys(cx, proxy, props);
931 }
933 bool
934 nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
935 unsigned flags, JS::MutableHandle<JS::Value> vp)
936 {
937 // BaseProxyHandler::iterate seems to do what we want here: fall
938 // back on the property names returned from keys() and enumerate().
939 return js::BaseProxyHandler::iterate(cx, proxy, flags, vp);
940 }
942 bool
943 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
944 JS::Handle<JSObject*> proxy,
945 JS::Handle<jsid> id,
946 JS::MutableHandle<JS::Value> vp,
947 bool& found)
948 {
949 nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id);
950 if (!frame) {
951 found = false;
952 return true;
953 }
955 found = true;
956 // Just return the window's global
957 nsGlobalWindow* global = static_cast<nsGlobalWindow*>(frame.get());
958 global->EnsureInnerWindow();
959 JSObject* obj = global->FastGetGlobalJSObject();
960 // This null check fixes a hard-to-reproduce crash that occurs when we
961 // get here when we're mid-call to nsDocShell::Destroy. See bug 640904
962 // comment 105.
963 if (MOZ_UNLIKELY(!obj)) {
964 return xpc::Throw(cx, NS_ERROR_FAILURE);
965 }
967 vp.setObject(*obj);
968 return JS_WrapValue(cx, vp);
969 }
971 already_AddRefed<nsIDOMWindow>
972 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
973 JS::Handle<JSObject*> proxy,
974 JS::Handle<jsid> id)
975 {
976 int32_t index = GetArrayIndexFromId(cx, id);
977 if (!IsArrayIndex(index)) {
978 return nullptr;
979 }
981 nsGlobalWindow* win = GetWindow(proxy);
982 bool unused;
983 return win->IndexedGetter(index, unused);
984 }
986 bool
987 nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
988 JS::AutoIdVector &props)
989 {
990 uint32_t length = GetWindow(proxy)->Length();
991 MOZ_ASSERT(int32_t(length) >= 0);
992 if (!props.reserve(props.length() + length)) {
993 return false;
994 }
995 for (int32_t i = 0; i < int32_t(length); ++i) {
996 props.append(INT_TO_JSID(i));
997 }
999 return true;
1000 }
1002 bool
1003 nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy,
1004 JS::Handle<jsid> id, JS::Handle<JSObject*> callable)
1005 {
1006 return js::WatchGuts(cx, proxy, id, callable);
1007 }
1009 bool
1010 nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
1011 JS::Handle<jsid> id)
1012 {
1013 return js::UnwatchGuts(cx, proxy, id);
1014 }
1016 nsOuterWindowProxy
1017 nsOuterWindowProxy::singleton;
1019 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
1020 {
1021 public:
1022 nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
1024 virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
1026 static nsChromeOuterWindowProxy singleton;
1027 };
1029 const char *
1030 nsChromeOuterWindowProxy::className(JSContext *cx,
1031 JS::Handle<JSObject*> proxy)
1032 {
1033 MOZ_ASSERT(js::IsProxy(proxy));
1035 return "ChromeWindow";
1036 }
1038 nsChromeOuterWindowProxy
1039 nsChromeOuterWindowProxy::singleton;
1041 static JSObject*
1042 NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> parent, bool isChrome)
1043 {
1044 JSAutoCompartment ac(cx, parent);
1045 js::WrapperOptions options;
1046 options.setClass(&OuterWindowProxyClass);
1047 options.setSingleton(true);
1048 JSObject *obj = js::Wrapper::New(cx, parent, parent,
1049 isChrome ? &nsChromeOuterWindowProxy::singleton
1050 : &nsOuterWindowProxy::singleton,
1051 &options);
1053 NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class");
1054 return obj;
1055 }
1057 //*****************************************************************************
1058 //*** nsGlobalWindow: Object Management
1059 //*****************************************************************************
1061 nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
1062 : nsPIDOMWindow(aOuterWindow),
1063 mIdleFuzzFactor(0),
1064 mIdleCallbackIndex(-1),
1065 mCurrentlyIdle(false),
1066 mAddActiveEventFuzzTime(true),
1067 mIsFrozen(false),
1068 mFullScreen(false),
1069 mIsClosed(false),
1070 mInClose(false),
1071 mHavePendingClose(false),
1072 mHadOriginalOpener(false),
1073 mIsPopupSpam(false),
1074 mBlockScriptedClosingFlag(false),
1075 mFireOfflineStatusChangeEventOnThaw(false),
1076 mNotifyIdleObserversIdleOnThaw(false),
1077 mNotifyIdleObserversActiveOnThaw(false),
1078 mCreatingInnerWindow(false),
1079 mIsChrome(false),
1080 mCleanMessageManager(false),
1081 mNeedsFocus(true),
1082 mHasFocus(false),
1083 #if defined(XP_MACOSX)
1084 mShowAccelerators(false),
1085 mShowFocusRings(false),
1086 #else
1087 mShowAccelerators(true),
1088 mShowFocusRings(true),
1089 #endif
1090 mShowFocusRingForContent(false),
1091 mFocusByKeyOccurred(false),
1092 mInnerObjectsFreed(false),
1093 mHasGamepad(false),
1094 #ifdef MOZ_GAMEPAD
1095 mHasSeenGamepadInput(false),
1096 #endif
1097 mNotifiedIDDestroyed(false),
1098 mAllowScriptsToClose(false),
1099 mTimeoutInsertionPoint(nullptr),
1100 mTimeoutPublicIdCounter(1),
1101 mTimeoutFiringDepth(0),
1102 mTimeoutsSuspendDepth(0),
1103 mFocusMethod(0),
1104 mSerial(0),
1105 #ifdef DEBUG
1106 mSetOpenerWindowCalled(false),
1107 #endif
1108 #ifdef MOZ_B2G
1109 mNetworkUploadObserverEnabled(false),
1110 mNetworkDownloadObserverEnabled(false),
1111 #endif
1112 mCleanedUp(false),
1113 mDialogAbuseCount(0),
1114 mAreDialogsEnabled(true)
1115 {
1116 nsLayoutStatics::AddRef();
1118 // Initialize the PRCList (this).
1119 PR_INIT_CLIST(this);
1121 if (aOuterWindow) {
1122 // |this| is an inner window, add this inner window to the outer
1123 // window list of inners.
1124 PR_INSERT_AFTER(this, aOuterWindow);
1126 mObserver = new nsGlobalWindowObserver(this);
1127 if (mObserver) {
1128 NS_ADDREF(mObserver);
1129 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1130 if (os) {
1131 // Watch for online/offline status changes so we can fire events. Use
1132 // a strong reference.
1133 os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
1134 false);
1136 // Watch for dom-storage2-changed so we can fire storage
1137 // events. Use a strong reference.
1138 os->AddObserver(mObserver, "dom-storage2-changed", false);
1139 }
1140 }
1141 } else {
1142 // |this| is an outer window. Outer windows start out frozen and
1143 // remain frozen until they get an inner window, so freeze this
1144 // outer window here.
1145 Freeze();
1147 mObserver = nullptr;
1148 SetIsDOMBinding();
1149 }
1151 // We could have failed the first time through trying
1152 // to create the entropy collector, so we should
1153 // try to get one until we succeed.
1155 gRefCnt++;
1157 if (gRefCnt == 1) {
1158 Preferences::AddIntVarCache(&gMinTimeoutValue,
1159 "dom.min_timeout_value",
1160 DEFAULT_MIN_TIMEOUT_VALUE);
1161 Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
1162 "dom.min_background_timeout_value",
1163 DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
1164 Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled,
1165 "dom.idle-observers-api.fuzz_time.disabled",
1166 false);
1167 }
1169 if (gDumpFile == nullptr) {
1170 const nsAdoptingCString& fname =
1171 Preferences::GetCString("browser.dom.window.dump.file");
1172 if (!fname.IsEmpty()) {
1173 // if this fails to open, Dump() knows to just go to stdout
1174 // on null.
1175 gDumpFile = fopen(fname, "wb+");
1176 } else {
1177 gDumpFile = stdout;
1178 }
1179 }
1181 mSerial = ++gSerialCounter;
1183 #ifdef DEBUG
1184 if (!PR_GetEnv("MOZ_QUIET")) {
1185 printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n",
1186 gRefCnt,
1187 static_cast<void*>(ToCanonicalSupports(this)),
1188 getpid(),
1189 gSerialCounter,
1190 static_cast<void*>(ToCanonicalSupports(aOuterWindow)));
1191 }
1192 #endif
1194 #ifdef PR_LOGGING
1195 if (gDOMLeakPRLog)
1196 PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
1197 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
1198 #endif
1200 NS_ASSERTION(sWindowsById, "Windows hash table must be created!");
1201 NS_ASSERTION(!sWindowsById->Get(mWindowID),
1202 "This window shouldn't be in the hash table yet!");
1203 // We seem to see crashes in release builds because of null |sWindowsById|.
1204 if (sWindowsById) {
1205 sWindowsById->Put(mWindowID, this);
1206 }
1207 }
1209 /* static */
1210 void
1211 nsGlobalWindow::Init()
1212 {
1213 CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
1214 NS_ASSERTION(gEntropyCollector,
1215 "gEntropyCollector should have been initialized!");
1217 #ifdef PR_LOGGING
1218 gDOMLeakPRLog = PR_NewLogModule("DOMLeak");
1219 NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");
1220 #endif
1222 sWindowsById = new WindowByIdTable();
1223 }
1225 static PLDHashOperator
1226 DisconnectEventTargetObjects(nsPtrHashKey<DOMEventTargetHelper>* aKey,
1227 void* aClosure)
1228 {
1229 nsRefPtr<DOMEventTargetHelper> target = aKey->GetKey();
1230 target->DisconnectFromOwner();
1231 return PL_DHASH_NEXT;
1232 }
1234 nsGlobalWindow::~nsGlobalWindow()
1235 {
1236 mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
1237 mEventTargetObjects.Clear();
1239 // We have to check if sWindowsById isn't null because ::Shutdown might have
1240 // been called.
1241 if (sWindowsById) {
1242 NS_ASSERTION(sWindowsById->Get(mWindowID),
1243 "This window should be in the hash table");
1244 sWindowsById->Remove(mWindowID);
1245 }
1247 --gRefCnt;
1249 #ifdef DEBUG
1250 if (!PR_GetEnv("MOZ_QUIET")) {
1251 nsAutoCString url;
1252 if (mLastOpenedURI) {
1253 mLastOpenedURI->GetSpec(url);
1255 // Data URLs can be very long, so truncate to avoid flooding the log.
1256 const uint32_t maxURLLength = 1000;
1257 if (url.Length() > maxURLLength) {
1258 url.Truncate(maxURLLength);
1259 }
1260 }
1262 nsGlobalWindow* outer = static_cast<nsGlobalWindow*>(mOuterWindow.get());
1263 printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n",
1264 gRefCnt,
1265 static_cast<void*>(ToCanonicalSupports(this)),
1266 getpid(),
1267 mSerial,
1268 static_cast<void*>(ToCanonicalSupports(outer)),
1269 url.get());
1270 }
1271 #endif
1273 #ifdef PR_LOGGING
1274 if (gDOMLeakPRLog)
1275 PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
1276 ("DOMWINDOW %p destroyed", this));
1277 #endif
1279 if (IsOuterWindow()) {
1280 JSObject *proxy = GetWrapperPreserveColor();
1281 if (proxy) {
1282 js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr));
1283 }
1285 // An outer window is destroyed with inner windows still possibly
1286 // alive, iterate through the inner windows and null out their
1287 // back pointer to this outer, and pull them out of the list of
1288 // inner windows.
1290 nsGlobalWindow *w;
1291 while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) {
1292 PR_REMOVE_AND_INIT_LINK(w);
1293 }
1295 DropOuterWindowDocs();
1296 } else {
1297 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1298 mMutationBits ? 1 : 0);
1300 if (mListenerManager) {
1301 mListenerManager->Disconnect();
1302 mListenerManager = nullptr;
1303 }
1305 // An inner window is destroyed, pull it out of the outer window's
1306 // list if inner windows.
1308 PR_REMOVE_LINK(this);
1310 // If our outer window's inner window is this window, null out the
1311 // outer window's reference to this window that's being deleted.
1312 nsGlobalWindow *outer = GetOuterWindowInternal();
1313 if (outer) {
1314 outer->MaybeClearInnerWindow(this);
1315 }
1316 }
1318 // Outer windows are always supposed to call CleanUp before letting themselves
1319 // be destroyed. And while CleanUp generally seems to be intended to clean up
1320 // outers, we've historically called it for both. Changing this would probably
1321 // involve auditing all of the references that inners and outers can have, and
1322 // separating the handling into CleanUp() and FreeInnerObjects.
1323 if (IsInnerWindow()) {
1324 CleanUp();
1325 } else {
1326 MOZ_ASSERT(mCleanedUp);
1327 }
1329 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1330 if (ac)
1331 ac->RemoveWindowAsListener(this);
1333 nsLayoutStatics::Release();
1334 }
1336 void
1337 nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject)
1338 {
1339 mEventTargetObjects.PutEntry(aObject);
1340 }
1342 void
1343 nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
1344 {
1345 mEventTargetObjects.RemoveEntry(aObject);
1346 }
1348 // static
1349 void
1350 nsGlobalWindow::ShutDown()
1351 {
1352 if (gDumpFile && gDumpFile != stdout) {
1353 fclose(gDumpFile);
1354 }
1355 gDumpFile = nullptr;
1357 NS_IF_RELEASE(gEntropyCollector);
1359 delete sWindowsById;
1360 sWindowsById = nullptr;
1361 }
1363 // static
1364 void
1365 nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow)
1366 {
1367 if (aWindow->mCachedXBLPrototypeHandlers &&
1368 aWindow->mCachedXBLPrototypeHandlers->Count() > 0) {
1369 aWindow->mCachedXBLPrototypeHandlers->Clear();
1370 }
1371 }
1373 void
1374 nsGlobalWindow::MaybeForgiveSpamCount()
1375 {
1376 if (IsOuterWindow() &&
1377 IsPopupSpamWindow())
1378 {
1379 SetPopupSpamWindow(false);
1380 --gOpenPopupSpamCount;
1381 NS_ASSERTION(gOpenPopupSpamCount >= 0,
1382 "Unbalanced decrement of gOpenPopupSpamCount");
1383 }
1384 }
1386 void
1387 nsGlobalWindow::DropOuterWindowDocs()
1388 {
1389 MOZ_ASSERT(IsOuterWindow());
1390 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
1391 mDoc = nullptr;
1392 mSuspendedDoc = nullptr;
1393 }
1395 void
1396 nsGlobalWindow::CleanUp()
1397 {
1398 // Guarantee idempotence.
1399 if (mCleanedUp)
1400 return;
1401 mCleanedUp = true;
1403 mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
1404 mEventTargetObjects.Clear();
1406 if (mObserver) {
1407 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1408 if (os) {
1409 os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
1410 os->RemoveObserver(mObserver, "dom-storage2-changed");
1411 }
1413 #ifdef MOZ_B2G
1414 DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
1415 DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
1416 #endif // MOZ_B2G
1418 if (mIdleService) {
1419 mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
1420 }
1422 // Drop its reference to this dying window, in case for some bogus reason
1423 // the object stays around.
1424 mObserver->Forget();
1425 NS_RELEASE(mObserver);
1426 }
1428 if (mNavigator) {
1429 mNavigator->Invalidate();
1430 mNavigator = nullptr;
1431 }
1433 mScreen = nullptr;
1434 mMenubar = nullptr;
1435 mToolbar = nullptr;
1436 mLocationbar = nullptr;
1437 mPersonalbar = nullptr;
1438 mStatusbar = nullptr;
1439 mScrollbars = nullptr;
1440 mLocation = nullptr;
1441 mHistory = nullptr;
1442 mFrames = nullptr;
1443 mWindowUtils = nullptr;
1444 mApplicationCache = nullptr;
1445 mIndexedDB = nullptr;
1447 mConsole = nullptr;
1449 mExternal = nullptr;
1451 mPerformance = nullptr;
1453 #ifdef MOZ_WEBSPEECH
1454 mSpeechSynthesis = nullptr;
1455 #endif
1457 ClearControllers();
1459 mOpener = nullptr; // Forces Release
1460 if (mContext) {
1461 mContext = nullptr; // Forces Release
1462 }
1463 mChromeEventHandler = nullptr; // Forces Release
1464 mParentTarget = nullptr;
1466 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
1468 if (inner) {
1469 inner->CleanUp();
1470 }
1472 DisableGamepadUpdates();
1473 mHasGamepad = false;
1475 if (mCleanMessageManager) {
1476 NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned");
1477 nsGlobalChromeWindow *asChrome = static_cast<nsGlobalChromeWindow*>(this);
1478 if (asChrome->mMessageManager) {
1479 static_cast<nsFrameMessageManager*>(
1480 asChrome->mMessageManager.get())->Disconnect();
1481 }
1482 }
1484 mArguments = nullptr;
1485 mDialogArguments = nullptr;
1487 CleanupCachedXBLHandlers(this);
1489 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1490 mAudioContexts[i]->Shutdown();
1491 }
1492 mAudioContexts.Clear();
1494 if (mIdleTimer) {
1495 mIdleTimer->Cancel();
1496 mIdleTimer = nullptr;
1497 }
1499 DisableTimeChangeNotifications();
1500 }
1502 void
1503 nsGlobalWindow::ClearControllers()
1504 {
1505 if (mControllers) {
1506 uint32_t count;
1507 mControllers->GetControllerCount(&count);
1509 while (count--) {
1510 nsCOMPtr<nsIController> controller;
1511 mControllers->GetControllerAt(count, getter_AddRefs(controller));
1513 nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
1514 if (context)
1515 context->SetCommandContext(nullptr);
1516 }
1518 mControllers = nullptr;
1519 }
1520 }
1522 void
1523 nsGlobalWindow::FreeInnerObjects()
1524 {
1525 NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
1527 // Make sure that this is called before we null out the document and
1528 // other members that the window destroyed observers could
1529 // re-create.
1530 NotifyDOMWindowDestroyed(this);
1532 mInnerObjectsFreed = true;
1534 // Kill all of the workers for this window.
1535 mozilla::dom::workers::CancelWorkersForWindow(this);
1537 // Close all offline storages for this window.
1538 quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
1539 if (quotaManager) {
1540 quotaManager->AbortCloseStoragesForWindow(this);
1541 }
1543 ClearAllTimeouts();
1545 if (mIdleTimer) {
1546 mIdleTimer->Cancel();
1547 mIdleTimer = nullptr;
1548 }
1550 mIdleObservers.Clear();
1552 mChromeEventHandler = nullptr;
1554 if (mListenerManager) {
1555 mListenerManager->Disconnect();
1556 mListenerManager = nullptr;
1557 }
1559 mLocation = nullptr;
1560 mHistory = nullptr;
1562 if (mNavigator) {
1563 mNavigator->OnNavigation();
1564 mNavigator->Invalidate();
1565 mNavigator = nullptr;
1566 }
1568 if (mScreen) {
1569 mScreen = nullptr;
1570 }
1572 if (mDoc) {
1573 // Remember the document's principal and URI.
1574 mDocumentPrincipal = mDoc->NodePrincipal();
1575 mDocumentURI = mDoc->GetDocumentURI();
1576 mDocBaseURI = mDoc->GetDocBaseURI();
1578 while (mDoc->EventHandlingSuppressed()) {
1579 mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false);
1580 }
1582 // Note: we don't have to worry about eAnimationsOnly suppressions because
1583 // they won't leak.
1584 }
1586 // Remove our reference to the document and the document principal.
1587 mFocusedNode = nullptr;
1589 if (mApplicationCache) {
1590 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect();
1591 mApplicationCache = nullptr;
1592 }
1594 mIndexedDB = nullptr;
1596 NotifyWindowIDDestroyed("inner-window-destroyed");
1598 CleanupCachedXBLHandlers(this);
1600 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1601 mAudioContexts[i]->Shutdown();
1602 }
1603 mAudioContexts.Clear();
1605 #ifdef MOZ_GAMEPAD
1606 DisableGamepadUpdates();
1607 mHasGamepad = false;
1608 mGamepads.Clear();
1609 #endif
1610 }
1612 //*****************************************************************************
1613 // nsGlobalWindow::nsISupports
1614 //*****************************************************************************
1616 DOMCI_DATA(Window, nsGlobalWindow)
1618 // QueryInterface implementation for nsGlobalWindow
1619 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow)
1620 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1621 // Make sure this matches the cast in nsGlobalWindow::FromWrapper()
1622 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget)
1623 NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
1624 #ifdef MOZ_B2G
1625 NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G)
1626 #endif // MOZ_B2G
1627 #ifdef MOZ_WEBSPEECH
1628 NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter)
1629 #endif // MOZ_B2G
1630 NS_INTERFACE_MAP_ENTRY(nsIDOMJSWindow)
1631 if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) {
1632 foundInterface = static_cast<nsIDOMWindowInternal*>(this);
1633 if (!sWarnedAboutWindowInternal) {
1634 sWarnedAboutWindowInternal = true;
1635 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1636 NS_LITERAL_CSTRING("Extensions"), mDoc,
1637 nsContentUtils::eDOM_PROPERTIES,
1638 "nsIDOMWindowInternalWarning");
1639 }
1640 } else
1641 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
1642 NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
1643 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
1644 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
1645 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1646 NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow)
1647 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
1648 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
1649 NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance)
1650 NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver)
1651 NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers)
1652 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window)
1653 NS_INTERFACE_MAP_END
1656 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
1657 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
1659 static PLDHashOperator
1660 MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
1661 {
1662 JS::ExposeObjectToActiveJS(aData);
1663 return PL_DHASH_NEXT;
1664 }
1666 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
1667 if (tmp->IsBlackForCC(false)) {
1668 if (tmp->mCachedXBLPrototypeHandlers) {
1669 tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr);
1670 }
1671 if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
1672 elm->MarkForCC();
1673 }
1674 tmp->UnmarkGrayTimers();
1675 return true;
1676 }
1677 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1679 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
1680 return tmp->IsBlackForCC(true);
1681 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1683 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow)
1684 return tmp->IsBlackForCC(false);
1685 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1687 inline void
1688 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
1689 IdleObserverHolder& aField,
1690 const char* aName,
1691 unsigned aFlags)
1692 {
1693 CycleCollectionNoteChild(aCallback, aField.mIdleObserver.get(), aName, aFlags);
1694 }
1696 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow)
1698 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
1699 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1700 char name[512];
1701 PR_snprintf(name, sizeof(name), "nsGlobalWindow #%ld", tmp->mWindowID);
1702 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1703 } else {
1704 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get())
1705 }
1707 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
1709 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
1710 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
1711 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
1712 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
1713 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
1715 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1717 #ifdef MOZ_WEBSPEECH
1718 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1719 #endif
1721 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1723 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1725 for (nsTimeout* timeout = tmp->mTimeouts.getFirst();
1726 timeout;
1727 timeout = timeout->getNext()) {
1728 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
1729 }
1731 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
1732 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
1733 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
1734 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
1735 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
1736 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
1737 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
1738 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
1740 #ifdef MOZ_GAMEPAD
1741 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1742 #endif
1744 // Traverse stuff from nsPIDOMWindow
1745 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
1746 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
1747 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement)
1748 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode)
1750 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
1751 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
1752 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
1753 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
1754 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
1755 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
1756 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
1757 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
1758 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
1759 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1760 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1762 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
1763 nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
1765 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
1767 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
1768 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
1769 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
1770 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
1771 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
1773 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1775 #ifdef MOZ_WEBSPEECH
1776 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1777 #endif
1779 if (tmp->mOuterWindow) {
1780 static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp);
1781 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1782 }
1784 if (tmp->mListenerManager) {
1785 tmp->mListenerManager->Disconnect();
1786 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
1787 }
1788 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage)
1789 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage)
1790 if (tmp->mApplicationCache) {
1791 static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect();
1792 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
1793 }
1794 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
1795 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
1796 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
1797 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents)
1798 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
1800 #ifdef MOZ_GAMEPAD
1801 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1802 #endif
1804 // Unlink stuff from nsPIDOMWindow
1805 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
1806 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
1807 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
1808 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode)
1810 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
1811 NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
1812 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
1813 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
1814 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
1815 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
1816 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
1817 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
1818 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
1819 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1820 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1822 #ifdef DEBUG
1823 void
1824 nsGlobalWindow::RiskyUnlink()
1825 {
1826 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1827 }
1828 #endif
1830 struct TraceData
1831 {
1832 const TraceCallbacks& callbacks;
1833 void* closure;
1834 };
1836 static PLDHashOperator
1837 TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
1838 {
1839 TraceData* data = static_cast<TraceData*>(aClosure);
1840 data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure);
1841 return PL_DHASH_NEXT;
1842 }
1844 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
1845 if (tmp->mCachedXBLPrototypeHandlers) {
1846 TraceData data = { aCallbacks, aClosure };
1847 tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data);
1848 }
1849 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1850 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1852 bool
1853 nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
1854 {
1855 if (!nsCCUncollectableMarker::sGeneration) {
1856 return false;
1857 }
1859 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1860 IsBlack()) &&
1861 (!aTracingNeeded ||
1862 HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
1863 }
1865 void
1866 nsGlobalWindow::UnmarkGrayTimers()
1867 {
1868 for (nsTimeout* timeout = mTimeouts.getFirst();
1869 timeout;
1870 timeout = timeout->getNext()) {
1871 if (timeout->mScriptHandler) {
1872 Function* f = timeout->mScriptHandler->GetCallback();
1873 if (f) {
1874 // Callable() already does xpc_UnmarkGrayObject.
1875 DebugOnly<JS::Handle<JSObject*> > o = f->Callable();
1876 MOZ_ASSERT(!xpc_IsGrayGCThing(o.value), "Should have been unmarked");
1877 }
1878 }
1879 }
1880 }
1882 //*****************************************************************************
1883 // nsGlobalWindow::nsIScriptGlobalObject
1884 //*****************************************************************************
1886 nsresult
1887 nsGlobalWindow::EnsureScriptEnvironment()
1888 {
1889 nsGlobalWindow* outer = GetOuterWindowInternal();
1890 if (!outer) {
1891 NS_WARNING("No outer window available!");
1892 return NS_ERROR_FAILURE;
1893 }
1895 if (outer->GetWrapperPreserveColor()) {
1896 return NS_OK;
1897 }
1899 NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(),
1900 "No cached wrapper, but we have an inner window?");
1902 // If this window is a [i]frame, don't bother GC'ing when the frame's context
1903 // is destroyed since a GC will happen when the frameset or host document is
1904 // destroyed anyway.
1905 nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer);
1907 NS_ASSERTION(!outer->mContext, "Will overwrite mContext!");
1909 // should probably assert the context is clean???
1910 context->WillInitializeContext();
1912 nsresult rv = context->InitContext();
1913 NS_ENSURE_SUCCESS(rv, rv);
1915 outer->mContext = context;
1916 return NS_OK;
1917 }
1919 nsIScriptContext *
1920 nsGlobalWindow::GetScriptContext()
1921 {
1922 nsGlobalWindow* outer = GetOuterWindowInternal();
1923 return outer ? outer->mContext : nullptr;
1924 }
1926 JSObject *
1927 nsGlobalWindow::GetGlobalJSObject()
1928 {
1929 return FastGetGlobalJSObject();
1930 }
1932 void
1933 nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
1934 {
1935 TraceWrapper(aTrc, "active window global");
1936 }
1938 /* static */
1939 JSObject*
1940 nsGlobalWindow::OuterObject(JSContext* aCx, JS::Handle<JSObject*> aObj)
1941 {
1942 nsGlobalWindow* origWin;
1943 UNWRAP_OBJECT(Window, aObj, origWin);
1944 nsGlobalWindow* win = origWin->GetOuterWindowInternal();
1946 if (!win) {
1947 // If we no longer have an outer window. No code should ever be
1948 // running on a window w/o an outer, which means this hook should
1949 // never be called when we have no outer. But just in case, return
1950 // null to prevent leaking an inner window to code in a different
1951 // window.
1952 NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
1953 return nullptr;
1954 }
1956 JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject());
1957 MOZ_ASSERT(winObj);
1959 // Note that while |wrapper| is same-compartment with cx, the outer window
1960 // might not be. If we're running script in an inactive scope and evalute
1961 // |this|, the outer window is actually a cross-compartment wrapper. So we
1962 // need to wrap here.
1963 if (!JS_WrapObject(aCx, &winObj)) {
1964 NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
1965 return nullptr;
1966 }
1968 return winObj;
1969 }
1971 bool
1972 nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
1973 {
1974 // We reuse the inner window when:
1975 // a. We are currently at our original document.
1976 // b. At least one of the following conditions are true:
1977 // -- The new document is the same as the old document. This means that we're
1978 // getting called from document.open().
1979 // -- The new document has the same origin as what we have loaded right now.
1981 if (!mDoc || !aNewDocument) {
1982 return false;
1983 }
1985 if (!mDoc->IsInitialDocument()) {
1986 return false;
1987 }
1989 NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()),
1990 "How'd this happen?");
1992 // Great, we're the original document, check for one of the other
1993 // conditions.
1995 if (mDoc == aNewDocument) {
1996 return true;
1997 }
1999 bool equal;
2000 if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(),
2001 &equal)) &&
2002 equal) {
2003 // The origin is the same.
2004 return true;
2005 }
2007 return false;
2008 }
2010 void
2011 nsGlobalWindow::SetInitialPrincipalToSubject()
2012 {
2013 FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ());
2015 // First, grab the subject principal.
2016 nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::GetSubjectPrincipal();
2017 if (!newWindowPrincipal) {
2018 newWindowPrincipal = nsContentUtils::GetSystemPrincipal();
2019 }
2021 // Now, if we're about to use the system principal or an nsExpandedPrincipal,
2022 // make sure we're not using it for a content docshell.
2023 if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) &&
2024 GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) {
2025 newWindowPrincipal = nullptr;
2026 }
2028 // If there's an existing document, bail if it either:
2029 if (mDoc) {
2030 // (a) is not an initial about:blank document, or
2031 if (!mDoc->IsInitialDocument())
2032 return;
2033 // (b) already has the correct principal.
2034 if (mDoc->NodePrincipal() == newWindowPrincipal)
2035 return;
2037 #ifdef DEBUG
2038 // If we have a document loaded at this point, it had better be about:blank.
2039 // Otherwise, something is really weird.
2040 nsCOMPtr<nsIURI> uri;
2041 mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri));
2042 NS_ASSERTION(uri && NS_IsAboutBlank(uri) &&
2043 NS_IsAboutBlank(mDoc->GetDocumentURI()),
2044 "Unexpected original document");
2045 #endif
2046 }
2048 GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal);
2049 mDoc->SetIsInitialDocument(true);
2051 nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell();
2053 if (shell && !shell->DidInitialize()) {
2054 // Ensure that if someone plays with this document they will get
2055 // layout happening.
2056 nsRect r = shell->GetPresContext()->GetVisibleArea();
2057 shell->Initialize(r.width, r.height);
2058 }
2059 }
2061 PopupControlState
2062 PushPopupControlState(PopupControlState aState, bool aForce)
2063 {
2064 MOZ_ASSERT(NS_IsMainThread());
2066 PopupControlState oldState = gPopupControlState;
2068 if (aState < gPopupControlState || aForce) {
2069 gPopupControlState = aState;
2070 }
2072 return oldState;
2073 }
2075 void
2076 PopPopupControlState(PopupControlState aState)
2077 {
2078 MOZ_ASSERT(NS_IsMainThread());
2080 gPopupControlState = aState;
2081 }
2083 PopupControlState
2084 nsGlobalWindow::PushPopupControlState(PopupControlState aState,
2085 bool aForce) const
2086 {
2087 return ::PushPopupControlState(aState, aForce);
2088 }
2090 void
2091 nsGlobalWindow::PopPopupControlState(PopupControlState aState) const
2092 {
2093 ::PopPopupControlState(aState);
2094 }
2096 PopupControlState
2097 nsGlobalWindow::GetPopupControlState() const
2098 {
2099 MOZ_ASSERT(NS_IsMainThread());
2100 return gPopupControlState;
2101 }
2103 #define WINDOWSTATEHOLDER_IID \
2104 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
2106 class WindowStateHolder MOZ_FINAL : public nsISupports
2107 {
2108 public:
2109 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
2110 NS_DECL_ISUPPORTS
2112 WindowStateHolder(nsGlobalWindow *aWindow);
2114 nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
2116 void DidRestoreWindow()
2117 {
2118 mInnerWindow = nullptr;
2119 }
2121 protected:
2122 ~WindowStateHolder();
2124 nsRefPtr<nsGlobalWindow> mInnerWindow;
2125 };
2127 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
2129 WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow)
2130 : mInnerWindow(aWindow)
2131 {
2132 NS_PRECONDITION(aWindow, "null window");
2133 NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
2135 // We hold onto this to make sure the inner window doesn't go away. The outer
2136 // window ends up recalculating it anyway.
2137 mInnerWindow->PreserveWrapper(ToSupports(mInnerWindow));
2139 aWindow->SuspendTimeouts();
2141 // When a global goes into the bfcache, we disable script.
2142 xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false);
2143 }
2145 WindowStateHolder::~WindowStateHolder()
2146 {
2147 if (mInnerWindow) {
2148 // This window was left in the bfcache and is now going away. We need to
2149 // free it up.
2150 // Note that FreeInnerObjects may already have been called on the
2151 // inner window if its outer has already had SetDocShell(null)
2152 // called.
2153 mInnerWindow->FreeInnerObjects();
2154 }
2155 }
2157 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
2159 // We need certain special behavior for remote XUL whitelisted domains, but we
2160 // don't want that behavior to take effect in automation, because we whitelist
2161 // all the mochitest domains. So we need to check a pref here.
2162 static bool
2163 TreatAsRemoteXUL(nsIPrincipal* aPrincipal)
2164 {
2165 MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(aPrincipal));
2166 return nsContentUtils::AllowXULXBLForPrincipal(aPrincipal) &&
2167 !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
2168 }
2170 /**
2171 * Create a new global object that will be used for an inner window.
2172 * Return the native global and an nsISupports 'holder' that can be used
2173 * to manage the lifetime of it.
2174 */
2175 static nsresult
2176 CreateNativeGlobalForInner(JSContext* aCx,
2177 nsGlobalWindow* aNewInner,
2178 nsIURI* aURI,
2179 nsIPrincipal* aPrincipal,
2180 JS::MutableHandle<JSObject*> aGlobal)
2181 {
2182 MOZ_ASSERT(aCx);
2183 MOZ_ASSERT(aNewInner);
2184 MOZ_ASSERT(aNewInner->IsInnerWindow());
2185 MOZ_ASSERT(aPrincipal);
2187 // DOMWindow with nsEP is not supported, we have to make sure
2188 // no one creates one accidentally.
2189 nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal);
2190 MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
2192 nsGlobalWindow *top = nullptr;
2193 if (aNewInner->GetOuterWindow()) {
2194 top = aNewInner->GetTop();
2195 }
2196 JS::CompartmentOptions options;
2197 if (top) {
2198 if (top->GetGlobalJSObject()) {
2199 options.setSameZoneAs(top->GetGlobalJSObject());
2200 }
2201 }
2203 nsIXPConnect* xpc = nsContentUtils::XPConnect();
2205 // Determine if we need the Components object.
2206 bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
2207 TreatAsRemoteXUL(aPrincipal);
2208 uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
2209 flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
2211 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
2212 nsresult rv = xpc->InitClassesWithNewWrappedGlobal(
2213 aCx, ToSupports(aNewInner),
2214 aPrincipal, flags, options, getter_AddRefs(holder));
2215 NS_ENSURE_SUCCESS(rv, rv);
2217 aGlobal.set(holder->GetJSObject());
2219 // Set the location information for the new global, so that tools like
2220 // about:memory may use that information
2221 MOZ_ASSERT(aGlobal);
2222 MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
2223 xpc::SetLocationForGlobal(aGlobal, aURI);
2225 return NS_OK;
2226 }
2228 nsresult
2229 nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
2230 nsISupports* aState,
2231 bool aForceReuseInnerWindow)
2232 {
2233 NS_PRECONDITION(mDocumentPrincipal == nullptr,
2234 "mDocumentPrincipal prematurely set!");
2235 MOZ_ASSERT(aDocument);
2237 if (IsInnerWindow()) {
2238 if (!mOuterWindow) {
2239 return NS_ERROR_NOT_INITIALIZED;
2240 }
2242 // Refuse to set a new document if the call came from an inner
2243 // window that's not the current inner window.
2244 if (mOuterWindow->GetCurrentInnerWindow() != this) {
2245 return NS_ERROR_NOT_AVAILABLE;
2246 }
2248 return GetOuterWindowInternal()->SetNewDocument(aDocument, aState,
2249 aForceReuseInnerWindow);
2250 }
2252 NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows");
2254 if (IsFrozen()) {
2255 // This outer is now getting its first inner, thaw the outer now
2256 // that it's ready and is getting an inner window.
2258 Thaw();
2259 }
2261 NS_ASSERTION(!GetCurrentInnerWindow() ||
2262 GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
2263 "Uh, mDoc doesn't match the current inner window "
2264 "document!");
2266 bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument);
2267 if (aForceReuseInnerWindow &&
2268 !wouldReuseInnerWindow &&
2269 mDoc &&
2270 mDoc->NodePrincipal() != aDocument->NodePrincipal()) {
2271 NS_ERROR("Attempted forced inner window reuse while changing principal");
2272 return NS_ERROR_UNEXPECTED;
2273 }
2275 nsCOMPtr<nsIDocument> oldDoc = mDoc;
2277 nsIScriptContext *scx = GetContextInternal();
2278 NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED);
2280 JSContext *cx = scx->GetNativeContext();
2282 #ifndef MOZ_DISABLE_CRYPTOLEGACY
2283 // clear smartcard events, our document has gone away.
2284 if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) {
2285 nsresult rv = mCrypto->SetEnableSmartCardEvents(false);
2286 NS_ENSURE_SUCCESS(rv, rv);
2287 }
2288 #endif
2290 if (!mDoc) {
2291 // First document load.
2293 // Get our private root. If it is equal to us, then we need to
2294 // attach our global key bindings that handles browser scrolling
2295 // and other browser commands.
2296 nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot();
2298 if (privateRoot == static_cast<nsIDOMWindow*>(this)) {
2299 nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler);
2300 }
2301 }
2303 /* No mDocShell means we're already been partially closed down. When that
2304 happens, setting status isn't a big requirement, so don't. (Doesn't happen
2305 under normal circumstances, but bug 49615 describes a case.) */
2307 nsContentUtils::AddScriptRunner(
2308 NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus));
2310 // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
2311 // window (see bug 776497). Be safe.
2312 bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
2313 GetCurrentInnerWindowInternal();
2315 nsresult rv = NS_OK;
2317 // Set mDoc even if this is an outer window to avoid
2318 // having to *always* reach into the inner window to find the
2319 // document.
2320 mDoc = aDocument;
2321 if (IsInnerWindow() && IsDOMBinding()) {
2322 WindowBinding::ClearCachedDocumentValue(cx, this);
2323 }
2325 // Take this opportunity to clear mSuspendedDoc. Our old inner window is now
2326 // responsible for unsuspending it.
2327 mSuspendedDoc = nullptr;
2329 #ifdef DEBUG
2330 mLastOpenedURI = aDocument->GetDocumentURI();
2331 #endif
2333 mContext->WillInitializeContext();
2335 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
2337 if (currentInner && currentInner->mNavigator) {
2338 currentInner->mNavigator->OnNavigation();
2339 }
2341 nsRefPtr<nsGlobalWindow> newInnerWindow;
2342 bool createdInnerWindow = false;
2344 bool thisChrome = IsChromeWindow();
2346 nsCxPusher cxPusher;
2347 cxPusher.Push(cx);
2349 // Check if we're near the stack limit before we get anywhere near the
2350 // transplanting code.
2351 JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
2353 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
2354 NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
2356 JS::Rooted<JSObject*> newInnerGlobal(cx);
2357 if (reUseInnerWindow) {
2358 // We're reusing the current inner window.
2359 NS_ASSERTION(!currentInner->IsFrozen(),
2360 "We should never be reusing a shared inner window");
2361 newInnerWindow = currentInner;
2362 newInnerGlobal = currentInner->GetWrapperPreserveColor();
2364 if (aDocument != oldDoc) {
2365 JS::ExposeObjectToActiveJS(newInnerGlobal);
2366 }
2368 // We're reusing the inner window, but this still counts as a navigation,
2369 // so all expandos and such defined on the outer window should go away. Force
2370 // all Xray wrappers to be recomputed.
2371 JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor());
2372 JS::ExposeObjectToActiveJS(rootedObject);
2373 if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
2374 return NS_ERROR_FAILURE;
2375 }
2377 // Inner windows are only reused for same-origin principals, but the principals
2378 // don't necessarily match exactly. Update the principal on the compartment to
2379 // match the new document.
2380 // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here
2381 // because we haven't yet set its mDoc to aDocument.
2382 JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal);
2383 #ifdef DEBUG
2384 bool sameOrigin = false;
2385 nsIPrincipal *existing =
2386 nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
2387 aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
2388 MOZ_ASSERT(sameOrigin);
2389 #endif
2390 JS_SetCompartmentPrincipals(compartment,
2391 nsJSPrincipals::get(aDocument->NodePrincipal()));
2392 } else {
2393 if (aState) {
2394 newInnerWindow = wsh->GetInnerWindow();
2395 newInnerGlobal = newInnerWindow->GetWrapperPreserveColor();
2396 } else {
2397 if (thisChrome) {
2398 newInnerWindow = new nsGlobalChromeWindow(this);
2399 } else if (mIsModalContentWindow) {
2400 newInnerWindow = new nsGlobalModalWindow(this);
2401 } else {
2402 newInnerWindow = new nsGlobalWindow(this);
2403 }
2405 // Freeze the outer window and null out the inner window so
2406 // that initializing classes on the new inner doesn't end up
2407 // reaching into the old inner window for classes etc.
2408 //
2409 // [This happens with Object.prototype when XPConnect creates
2410 // a temporary global while initializing classes; the reason
2411 // being that xpconnect creates the temp global w/o a parent
2412 // and proto, which makes the JS engine look up classes in
2413 // cx->globalObject, i.e. this outer window].
2415 mInnerWindow = nullptr;
2417 Freeze();
2418 mCreatingInnerWindow = true;
2419 // Every script context we are initialized with must create a
2420 // new global.
2421 rv = CreateNativeGlobalForInner(cx, newInnerWindow,
2422 aDocument->GetDocumentURI(),
2423 aDocument->NodePrincipal(),
2424 &newInnerGlobal);
2425 NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal &&
2426 newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
2427 "Failed to get script global");
2429 mCreatingInnerWindow = false;
2430 createdInnerWindow = true;
2431 Thaw();
2433 NS_ENSURE_SUCCESS(rv, rv);
2434 }
2436 if (currentInner && currentInner->GetWrapperPreserveColor()) {
2437 if (oldDoc == aDocument) {
2438 // Move the navigator from the old inner window to the new one since
2439 // this is a document.write. This is safe from a same-origin point of
2440 // view because document.write can only be used by the same origin.
2441 newInnerWindow->mNavigator = currentInner->mNavigator;
2442 currentInner->mNavigator = nullptr;
2443 if (newInnerWindow->mNavigator) {
2444 newInnerWindow->mNavigator->SetWindow(newInnerWindow);
2445 }
2447 // Make a copy of the old window's performance object on document.open.
2448 // Note that we have to force eager creation of it here, because we need
2449 // to grab the current document channel and whatnot before that changes.
2450 currentInner->CreatePerformanceObjectIfNeeded();
2451 if (currentInner->mPerformance) {
2452 newInnerWindow->mPerformance =
2453 new nsPerformance(newInnerWindow,
2454 currentInner->mPerformance->GetDOMTiming(),
2455 currentInner->mPerformance->GetChannel(),
2456 currentInner->mPerformance->GetParentPerformance());
2457 }
2458 }
2460 // Don't free objects on our current inner window if it's going to be
2461 // held in the bfcache.
2462 if (!currentInner->IsFrozen()) {
2463 currentInner->FreeInnerObjects();
2464 }
2465 }
2467 mInnerWindow = newInnerWindow;
2469 if (!GetWrapperPreserveColor()) {
2470 JS::Rooted<JSObject*> outer(cx,
2471 NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2472 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
2474 js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
2476 // Inform the nsJSContext, which is the canonical holder of the outer.
2477 mContext->SetWindowProxy(outer);
2478 mContext->DidInitializeContext();
2480 SetWrapper(mContext->GetWindowProxy());
2481 } else {
2482 JS::ExposeObjectToActiveJS(newInnerGlobal);
2483 JS::Rooted<JSObject*> outerObject(cx,
2484 NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2485 if (!outerObject) {
2486 NS_ERROR("out of memory");
2487 return NS_ERROR_FAILURE;
2488 }
2490 JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
2492 js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
2494 outerObject = xpc::TransplantObject(cx, obj, outerObject);
2495 if (!outerObject) {
2496 NS_ERROR("unable to transplant wrappers, probably OOM");
2497 return NS_ERROR_FAILURE;
2498 }
2500 js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
2502 SetWrapper(outerObject);
2504 {
2505 JSAutoCompartment ac(cx, outerObject);
2507 JS_SetParent(cx, outerObject, newInnerGlobal);
2509 // Inform the nsJSContext, which is the canonical holder of the outer.
2510 mContext->SetWindowProxy(outerObject);
2512 NS_ASSERTION(!JS_IsExceptionPending(cx),
2513 "We might overwrite a pending exception!");
2514 XPCWrappedNativeScope* scope = xpc::GetObjectScope(outerObject);
2515 if (scope->mWaiverWrapperMap) {
2516 scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal);
2517 }
2518 }
2519 }
2521 // Enter the new global's compartment.
2522 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
2524 // Set scriptability based on the state of the docshell.
2525 bool allow = GetDocShell()->GetCanExecuteScripts();
2526 xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
2528 // If we created a new inner window above, we need to do the last little bit
2529 // of initialization now that the dust has settled.
2530 if (createdInnerWindow) {
2531 nsIXPConnect *xpc = nsContentUtils::XPConnect();
2532 nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
2533 nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal,
2534 getter_AddRefs(wrapper));
2535 NS_ENSURE_SUCCESS(rv, rv);
2536 NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
2537 rv = wrapper->FinishInitForWrappedGlobal();
2538 NS_ENSURE_SUCCESS(rv, rv);
2539 }
2541 if (!aState) {
2542 JS::Rooted<JSObject*> rootedWrapper(cx, GetWrapperPreserveColor());
2543 if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper,
2544 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
2545 JS_PropertyStub, JS_StrictPropertyStub)) {
2546 NS_ERROR("can't create the 'window' property");
2547 return NS_ERROR_FAILURE;
2548 }
2549 }
2550 }
2552 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
2554 if (!aState && !reUseInnerWindow) {
2555 // Loading a new page and creating a new inner window, *not*
2556 // restoring from session history.
2558 // Now that both the the inner and outer windows are initialized
2559 // let the script context do its magic to hook them together.
2560 MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
2561 #ifdef DEBUG
2562 JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
2563 JS::Rooted<JSObject*> proto1(cx), proto2(cx);
2564 JS_GetPrototype(cx, rootedJSObject, &proto1);
2565 JS_GetPrototype(cx, newInnerGlobal, &proto2);
2566 NS_ASSERTION(proto1 == proto2,
2567 "outer and inner globals should have the same prototype");
2568 #endif
2570 nsCOMPtr<Element> frame = GetFrameElementInternal();
2571 if (frame) {
2572 nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow();
2573 if (parentWindow && parentWindow->TimeoutSuspendCount()) {
2574 SuspendTimeouts(parentWindow->TimeoutSuspendCount());
2575 }
2576 }
2577 }
2579 // Add an extra ref in case we release mContext during GC.
2580 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
2582 aDocument->SetScriptGlobalObject(newInnerWindow);
2584 if (!aState) {
2585 if (reUseInnerWindow) {
2586 if (newInnerWindow->mDoc != aDocument) {
2587 newInnerWindow->mDoc = aDocument;
2589 if (newInnerWindow->IsDOMBinding()) {
2590 WindowBinding::ClearCachedDocumentValue(cx, newInnerWindow);
2591 } else {
2592 // We're reusing the inner window for a new document. In this
2593 // case we don't clear the inner window's scope, but we must
2594 // make sure the cached document property gets updated.
2596 JS::Rooted<JSObject*> obj(cx,
2597 currentInner->GetWrapperPreserveColor());
2598 ::JS_DeleteProperty(cx, obj, "document");
2599 }
2600 }
2601 } else {
2602 newInnerWindow->InnerSetNewDocument(cx, aDocument);
2604 // Initialize DOM classes etc on the inner window.
2605 JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
2606 rv = mContext->InitClasses(obj);
2607 NS_ENSURE_SUCCESS(rv, rv);
2608 }
2610 // If the document comes from a JAR, check if the channel was determined
2611 // to be unsafe. If so, permanently disable script on the compartment by
2612 // calling Block() and throwing away the key.
2613 nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
2614 if (jarChannel && jarChannel->GetIsUnsafe()) {
2615 xpc::Scriptability::Get(newInnerGlobal).Block();
2616 }
2618 if (mArguments) {
2619 newInnerWindow->DefineArgumentsProperty(mArguments);
2620 mArguments = nullptr;
2621 }
2623 // Give the new inner window our chrome event handler (since it
2624 // doesn't have one).
2625 newInnerWindow->mChromeEventHandler = mChromeEventHandler;
2626 }
2628 mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
2629 mContext->DidInitializeContext();
2631 // We wait to fire the debugger hook until the window is all set up and hooked
2632 // up with the outer. See bug 969156.
2633 if (createdInnerWindow) {
2634 JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper());
2635 JS_FireOnNewGlobalObject(cx, global);
2636 }
2638 if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
2639 // We should probably notify. However if this is the, arguably bad,
2640 // situation when we're creating a temporary non-chrome-about-blank
2641 // document in a chrome docshell, don't notify just yet. Instead wait
2642 // until we have a real chrome doc.
2643 if (!mDocShell ||
2644 mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome ||
2645 nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) {
2646 newInnerWindow->mHasNotifiedGlobalCreated = true;
2647 nsContentUtils::AddScriptRunner(
2648 NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated));
2649 }
2650 }
2652 PreloadLocalStorage();
2654 return NS_OK;
2655 }
2657 void
2658 nsGlobalWindow::PreloadLocalStorage()
2659 {
2660 if (!Preferences::GetBool(kStorageEnabled)) {
2661 return;
2662 }
2664 if (IsChromeWindow()) {
2665 return;
2666 }
2668 nsIPrincipal* principal = GetPrincipal();
2669 if (!principal) {
2670 return;
2671 }
2673 nsresult rv;
2674 nsCOMPtr<nsIURI> firstPartyIsolationURI;
2675 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
2676 if (NS_FAILED(rv)) {
2677 return;
2678 }
2680 nsCOMPtr<nsIDOMStorageManager> storageManager =
2681 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
2682 if (NS_FAILED(rv)) {
2683 return;
2684 }
2686 storageManager->PrecacheStorageForFirstParty(firstPartyIsolationURI, principal);
2687 }
2689 void
2690 nsGlobalWindow::DispatchDOMWindowCreated()
2691 {
2692 if (!mDoc) {
2693 return;
2694 }
2696 // Fire DOMWindowCreated at chrome event listeners
2697 nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"),
2698 true /* bubbles */,
2699 false /* not cancellable */);
2701 nsCOMPtr<nsIObserverService> observerService =
2702 mozilla::services::GetObserverService();
2703 if (observerService) {
2704 nsAutoString origin;
2705 nsIPrincipal* principal = mDoc->NodePrincipal();
2706 nsContentUtils::GetUTFOrigin(principal, origin);
2707 observerService->
2708 NotifyObservers(static_cast<nsIDOMWindow*>(this),
2709 nsContentUtils::IsSystemPrincipal(principal) ?
2710 "chrome-document-global-created" :
2711 "content-document-global-created",
2712 origin.get());
2713 }
2714 }
2716 void
2717 nsGlobalWindow::ClearStatus()
2718 {
2719 SetStatus(EmptyString());
2720 }
2722 void
2723 nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument)
2724 {
2725 NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows");
2726 MOZ_ASSERT(aDocument);
2728 #ifdef PR_LOGGING
2729 if (gDOMLeakPRLog && PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
2730 nsIURI *uri = aDocument->GetDocumentURI();
2731 nsAutoCString spec;
2732 if (uri)
2733 uri->GetSpec(spec);
2734 PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get());
2735 }
2736 #endif
2738 mDoc = aDocument;
2739 if (IsDOMBinding()) {
2740 WindowBinding::ClearCachedDocumentValue(aCx, this);
2741 }
2742 mFocusedNode = nullptr;
2743 mLocalStorage = nullptr;
2744 mSessionStorage = nullptr;
2746 #ifdef DEBUG
2747 mLastOpenedURI = aDocument->GetDocumentURI();
2748 #endif
2750 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
2751 mMutationBits ? 1 : 0);
2753 // Clear our mutation bitfield.
2754 mMutationBits = 0;
2755 }
2757 void
2758 nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
2759 {
2760 NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!");
2761 MOZ_ASSERT(aDocShell);
2763 if (aDocShell == mDocShell) {
2764 return;
2765 }
2767 mDocShell = aDocShell; // Weak Reference
2769 NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
2771 if (mFrames) {
2772 mFrames->SetDocShell(aDocShell);
2773 }
2775 // Get our enclosing chrome shell and retrieve its global window impl, so
2776 // that we can do some forwarding to the chrome document.
2777 nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
2778 mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2779 mChromeEventHandler = do_QueryInterface(chromeEventHandler);
2780 if (!mChromeEventHandler) {
2781 // We have no chrome event handler. If we have a parent,
2782 // get our chrome event handler from the parent. If
2783 // we don't have a parent, then we need to make a new
2784 // window root object that will function as a chrome event
2785 // handler and receive all events that occur anywhere inside
2786 // our window.
2787 nsCOMPtr<nsIDOMWindow> parentWindow;
2788 GetParent(getter_AddRefs(parentWindow));
2789 if (parentWindow.get() != static_cast<nsIDOMWindow*>(this)) {
2790 nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(parentWindow));
2791 mChromeEventHandler = piWindow->GetChromeEventHandler();
2792 }
2793 else {
2794 mChromeEventHandler = NS_NewWindowRoot(this);
2795 }
2796 }
2798 bool docShellActive;
2799 mDocShell->GetIsActive(&docShellActive);
2800 mIsBackground = !docShellActive;
2801 }
2803 void
2804 nsGlobalWindow::DetachFromDocShell()
2805 {
2806 NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");
2808 // DetachFromDocShell means the window is being torn down. Drop our
2809 // reference to the script context, allowing it to be deleted
2810 // later. Meanwhile, keep our weak reference to the script object
2811 // so that it can be retrieved later (until it is finalized by the JS GC).
2813 NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!");
2815 // Call FreeInnerObjects on all inner windows, not just the current
2816 // one, since some could be held by WindowStateHolder objects that
2817 // are GC-owned.
2818 for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
2819 inner != this;
2820 inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
2821 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
2822 "bad outer window pointer");
2823 inner->FreeInnerObjects();
2824 }
2826 // Make sure that this is called before we null out the document.
2827 NotifyDOMWindowDestroyed(this);
2829 NotifyWindowIDDestroyed("outer-window-destroyed");
2831 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
2833 if (currentInner) {
2834 NS_ASSERTION(mDoc, "Must have doc!");
2836 // Remember the document's principal and URI.
2837 mDocumentPrincipal = mDoc->NodePrincipal();
2838 mDocumentURI = mDoc->GetDocumentURI();
2839 mDocBaseURI = mDoc->GetDocBaseURI();
2841 // Release our document reference
2842 DropOuterWindowDocs();
2843 mFocusedNode = nullptr;
2844 }
2846 ClearControllers();
2848 mChromeEventHandler = nullptr; // force release now
2850 if (mContext) {
2851 mContext->GC(JS::gcreason::SET_DOC_SHELL);
2852 mContext = nullptr;
2853 }
2855 mDocShell = nullptr; // Weak Reference
2857 NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
2859 if (mFrames) {
2860 mFrames->SetDocShell(nullptr);
2861 }
2863 MaybeForgiveSpamCount();
2864 CleanUp();
2865 }
2867 void
2868 nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener,
2869 bool aOriginalOpener)
2870 {
2871 FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));
2873 NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled,
2874 "aOriginalOpener is true, but not first call to "
2875 "SetOpenerWindow!");
2876 NS_ASSERTION(aOpener || !aOriginalOpener,
2877 "Shouldn't set mHadOriginalOpener if aOpener is null");
2879 mOpener = do_GetWeakReference(aOpener);
2880 NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
2882 if (aOriginalOpener) {
2883 mHadOriginalOpener = true;
2884 }
2886 #ifdef DEBUG
2887 mSetOpenerWindowCalled = true;
2888 #endif
2889 }
2891 static
2892 already_AddRefed<EventTarget>
2893 TryGetTabChildGlobalAsEventTarget(nsISupports *aFrom)
2894 {
2895 nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(aFrom);
2896 if (!frameLoaderOwner) {
2897 return nullptr;
2898 }
2900 nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
2901 if (!frameLoader) {
2902 return nullptr;
2903 }
2905 nsCOMPtr<EventTarget> target = frameLoader->GetTabChildGlobalAsEventTarget();
2906 return target.forget();
2907 }
2909 void
2910 nsGlobalWindow::UpdateParentTarget()
2911 {
2912 // Try to get our frame element's tab child global (its in-process message
2913 // manager). If that fails, fall back to the chrome event handler's tab
2914 // child global, and if it doesn't have one, just use the chrome event
2915 // handler itself.
2917 nsCOMPtr<Element> frameElement = GetFrameElementInternal();
2918 nsCOMPtr<EventTarget> eventTarget =
2919 TryGetTabChildGlobalAsEventTarget(frameElement);
2921 if (!eventTarget) {
2922 nsGlobalWindow* topWin = GetScriptableTop();
2923 if (topWin) {
2924 frameElement = topWin->GetFrameElementInternal();
2925 eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement);
2926 }
2927 }
2929 if (!eventTarget) {
2930 eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler);
2931 }
2933 if (!eventTarget) {
2934 eventTarget = mChromeEventHandler;
2935 }
2937 mParentTarget = eventTarget;
2938 }
2940 EventTarget*
2941 nsGlobalWindow::GetTargetForDOMEvent()
2942 {
2943 return GetOuterWindowInternal();
2944 }
2946 EventTarget*
2947 nsGlobalWindow::GetTargetForEventTargetChain()
2948 {
2949 return IsInnerWindow() ? this : GetCurrentInnerWindowInternal();
2950 }
2952 nsresult
2953 nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor)
2954 {
2955 return NS_OK;
2956 }
2958 JSContext*
2959 nsGlobalWindow::GetJSContextForEventHandlers()
2960 {
2961 return nullptr;
2962 }
2964 nsresult
2965 nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor)
2966 {
2967 NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?");
2968 static uint32_t count = 0;
2969 uint32_t msg = aVisitor.mEvent->message;
2971 aVisitor.mCanHandle = true;
2972 aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119
2973 if ((msg == NS_MOUSE_MOVE) && gEntropyCollector) {
2974 //Chances are this counter will overflow during the life of the
2975 //process, but that's OK for our case. Means we get a little
2976 //more entropy.
2977 if (count++ % 100 == 0) {
2978 //Since the high bits seem to be zero's most of the time,
2979 //let's only take the lowest half of the point structure.
2980 int16_t myCoord[2];
2982 myCoord[0] = aVisitor.mEvent->refPoint.x;
2983 myCoord[1] = aVisitor.mEvent->refPoint.y;
2984 gEntropyCollector->RandomUpdate((void*)myCoord, sizeof(myCoord));
2985 gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time),
2986 sizeof(uint32_t));
2987 }
2988 } else if (msg == NS_RESIZE_EVENT) {
2989 mIsHandlingResizeEvent = true;
2990 } else if (msg == NS_MOUSE_BUTTON_DOWN &&
2991 aVisitor.mEvent->mFlags.mIsTrusted) {
2992 gMouseDown = true;
2993 } else if ((msg == NS_MOUSE_BUTTON_UP ||
2994 msg == NS_DRAGDROP_END) &&
2995 aVisitor.mEvent->mFlags.mIsTrusted) {
2996 gMouseDown = false;
2997 if (gDragServiceDisabled) {
2998 nsCOMPtr<nsIDragService> ds =
2999 do_GetService("@mozilla.org/widget/dragservice;1");
3000 if (ds) {
3001 gDragServiceDisabled = false;
3002 ds->Unsuppress();
3003 }
3004 }
3005 }
3007 aVisitor.mParentTarget = GetParentTarget();
3009 // Handle 'active' event.
3010 if (!mIdleObservers.IsEmpty() &&
3011 aVisitor.mEvent->mFlags.mIsTrusted &&
3012 (aVisitor.mEvent->HasMouseEventMessage() ||
3013 aVisitor.mEvent->HasDragEventMessage())) {
3014 mAddActiveEventFuzzTime = false;
3015 }
3017 return NS_OK;
3018 }
3020 bool
3021 nsGlobalWindow::ShouldPromptToBlockDialogs()
3022 {
3023 MOZ_ASSERT(IsOuterWindow());
3025 nsGlobalWindow *topWindow = GetScriptableTop();
3026 if (!topWindow) {
3027 NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?");
3028 return true;
3029 }
3031 topWindow = topWindow->GetCurrentInnerWindowInternal();
3032 if (!topWindow) {
3033 return true;
3034 }
3036 return topWindow->DialogsAreBeingAbused();
3037 }
3039 bool
3040 nsGlobalWindow::AreDialogsEnabled()
3041 {
3042 nsGlobalWindow *topWindow = GetScriptableTop();
3043 if (!topWindow) {
3044 NS_ERROR("AreDialogsEnabled() called without a top window?");
3045 return false;
3046 }
3048 // TODO: Warn if no top window?
3049 topWindow = topWindow->GetCurrentInnerWindowInternal();
3050 if (!topWindow) {
3051 return false;
3052 }
3054 // Dialogs are blocked if the content viewer is hidden
3055 if (mDocShell) {
3056 nsCOMPtr<nsIContentViewer> cv;
3057 mDocShell->GetContentViewer(getter_AddRefs(cv));
3059 bool isHidden;
3060 cv->GetIsHidden(&isHidden);
3061 if (isHidden) {
3062 return false;
3063 }
3064 }
3066 return topWindow->mAreDialogsEnabled;
3067 }
3069 bool
3070 nsGlobalWindow::DialogsAreBeingAbused()
3071 {
3072 MOZ_ASSERT(IsInnerWindow());
3073 NS_ASSERTION(GetScriptableTop() &&
3074 GetScriptableTop()->GetCurrentInnerWindowInternal() == this,
3075 "DialogsAreBeingAbused called with invalid window");
3077 if (mLastDialogQuitTime.IsNull() ||
3078 nsContentUtils::IsCallerChrome()) {
3079 return false;
3080 }
3082 TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime);
3083 if (dialogInterval.ToSeconds() <
3084 Preferences::GetInt("dom.successive_dialog_time_limit",
3085 DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) {
3086 mDialogAbuseCount++;
3088 return GetPopupControlState() > openAllowed ||
3089 mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
3090 }
3092 // Reset the abuse counter
3093 mDialogAbuseCount = 0;
3095 return false;
3096 }
3098 bool
3099 nsGlobalWindow::ConfirmDialogIfNeeded()
3100 {
3101 MOZ_ASSERT(IsOuterWindow());
3103 NS_ENSURE_TRUE(mDocShell, false);
3104 nsCOMPtr<nsIPromptService> promptSvc =
3105 do_GetService("@mozilla.org/embedcomp/prompt-service;1");
3107 if (!promptSvc) {
3108 return true;
3109 }
3111 // Reset popup state while opening a modal dialog, and firing events
3112 // about the dialog, to prevent the current state from being active
3113 // the whole time a modal dialog is open.
3114 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
3116 bool disableDialog = false;
3117 nsXPIDLString label, title;
3118 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
3119 "ScriptDialogLabel", label);
3120 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
3121 "ScriptDialogPreventTitle", title);
3122 promptSvc->Confirm(this, title.get(), label.get(), &disableDialog);
3123 if (disableDialog) {
3124 DisableDialogs();
3125 return false;
3126 }
3128 return true;
3129 }
3131 void
3132 nsGlobalWindow::DisableDialogs()
3133 {
3134 nsGlobalWindow *topWindow = GetScriptableTop();
3135 if (!topWindow) {
3136 NS_ERROR("DisableDialogs() called without a top window?");
3137 return;
3138 }
3140 topWindow = topWindow->GetCurrentInnerWindowInternal();
3141 // TODO: Warn if no top window?
3142 if (topWindow) {
3143 topWindow->mAreDialogsEnabled = false;
3144 }
3145 }
3147 void
3148 nsGlobalWindow::EnableDialogs()
3149 {
3150 nsGlobalWindow *topWindow = GetScriptableTop();
3151 if (!topWindow) {
3152 NS_ERROR("EnableDialogs() called without a top window?");
3153 return;
3154 }
3156 // TODO: Warn if no top window?
3157 topWindow = topWindow->GetCurrentInnerWindowInternal();
3158 if (topWindow) {
3159 topWindow->mAreDialogsEnabled = true;
3160 }
3161 }
3163 nsresult
3164 nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor)
3165 {
3166 NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?");
3168 // Return early if there is nothing to do.
3169 switch (aVisitor.mEvent->message) {
3170 case NS_RESIZE_EVENT:
3171 case NS_PAGE_UNLOAD:
3172 case NS_LOAD:
3173 break;
3174 default:
3175 return NS_OK;
3176 }
3178 /* mChromeEventHandler and mContext go dangling in the middle of this
3179 function under some circumstances (events that destroy the window)
3180 without this addref. */
3181 nsCOMPtr<nsIDOMEventTarget> kungFuDeathGrip1(mChromeEventHandler);
3182 nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal());
3184 if (aVisitor.mEvent->message == NS_RESIZE_EVENT) {
3185 mIsHandlingResizeEvent = false;
3186 } else if (aVisitor.mEvent->message == NS_PAGE_UNLOAD &&
3187 aVisitor.mEvent->mFlags.mIsTrusted) {
3188 // Execute bindingdetached handlers before we tear ourselves
3189 // down.
3190 if (mDoc) {
3191 mDoc->BindingManager()->ExecuteDetachedHandlers();
3192 }
3193 mIsDocumentLoaded = false;
3194 } else if (aVisitor.mEvent->message == NS_LOAD &&
3195 aVisitor.mEvent->mFlags.mIsTrusted) {
3196 // This is page load event since load events don't propagate to |window|.
3197 // @see nsDocument::PreHandleEvent.
3198 mIsDocumentLoaded = true;
3200 nsCOMPtr<Element> element = GetFrameElementInternal();
3201 nsIDocShell* docShell = GetDocShell();
3202 if (element && GetParentInternal() &&
3203 docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
3204 // If we're not in chrome, or at a chrome boundary, fire the
3205 // onload event for the frame element.
3207 nsEventStatus status = nsEventStatus_eIgnore;
3208 WidgetEvent event(aVisitor.mEvent->mFlags.mIsTrusted, NS_LOAD);
3209 event.mFlags.mBubbles = false;
3211 // Most of the time we could get a pres context to pass in here,
3212 // but not always (i.e. if this window is not shown there won't
3213 // be a pres context available). Since we're not firing a GUI
3214 // event we don't need a pres context anyway so we just pass
3215 // null as the pres context all the time here.
3216 EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
3217 }
3218 }
3220 return NS_OK;
3221 }
3223 nsresult
3224 nsGlobalWindow::DispatchDOMEvent(WidgetEvent* aEvent,
3225 nsIDOMEvent* aDOMEvent,
3226 nsPresContext* aPresContext,
3227 nsEventStatus* aEventStatus)
3228 {
3229 return EventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this),
3230 aEvent, aDOMEvent, aPresContext,
3231 aEventStatus);
3232 }
3234 void
3235 nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
3236 {
3237 MOZ_ASSERT(IsOuterWindow());
3238 if (aObject == GetWrapperPreserveColor()) {
3239 PoisonWrapper();
3240 }
3241 }
3243 nsresult
3244 nsGlobalWindow::SetArguments(nsIArray *aArguments)
3245 {
3246 MOZ_ASSERT(IsOuterWindow());
3247 nsresult rv;
3249 // Historically, we've used the same machinery to handle openDialog arguments
3250 // (exposed via window.arguments) and showModalDialog arguments (exposed via
3251 // window.dialogArguments), even though the former is XUL-only and uses an XPCOM
3252 // array while the latter is web-exposed and uses an arbitrary JS value.
3253 // Moreover, per-spec |dialogArguments| is a property of the browsing context
3254 // (outer), whereas |arguments| lives on the inner.
3255 //
3256 // We've now mostly separated them, but the difference is still opaque to
3257 // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
3258 // embedding waltz we do here).
3259 //
3260 // So we need to demultiplex the two cases here.
3261 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
3262 if (mIsModalContentWindow) {
3263 // nsWindowWatcher blindly converts the original nsISupports into an array
3264 // of length 1. We need to recover it, and then cast it back to the concrete
3265 // object we know it to be.
3266 nsCOMPtr<nsISupports> supports = do_QueryElementAt(aArguments, 0, &rv);
3267 NS_ENSURE_SUCCESS(rv, rv);
3268 mDialogArguments = static_cast<DialogValueHolder*>(supports.get());
3269 } else {
3270 mArguments = aArguments;
3271 rv = currentInner->DefineArgumentsProperty(aArguments);
3272 NS_ENSURE_SUCCESS(rv, rv);
3273 }
3275 return NS_OK;
3276 }
3278 nsresult
3279 nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
3280 {
3281 MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
3282 nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
3283 NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
3284 AutoJSContext cx;
3286 JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
3287 return ctx->SetProperty(obj, "arguments", aArguments);
3288 }
3290 //*****************************************************************************
3291 // nsGlobalWindow::nsIScriptObjectPrincipal
3292 //*****************************************************************************
3294 nsIPrincipal*
3295 nsGlobalWindow::GetPrincipal()
3296 {
3297 if (mDoc) {
3298 // If we have a document, get the principal from the document
3299 return mDoc->NodePrincipal();
3300 }
3302 if (mDocumentPrincipal) {
3303 return mDocumentPrincipal;
3304 }
3306 // If we don't have a principal and we don't have a document we
3307 // ask the parent window for the principal. This can happen when
3308 // loading a frameset that has a <frame src="javascript:xxx">, in
3309 // that case the global window is used in JS before we've loaded
3310 // a document into the window.
3312 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
3313 do_QueryInterface(GetParentInternal());
3315 if (objPrincipal) {
3316 return objPrincipal->GetPrincipal();
3317 }
3319 return nullptr;
3320 }
3322 //*****************************************************************************
3323 // nsGlobalWindow::nsIDOMWindow
3324 //*****************************************************************************
3326 nsIURI*
3327 nsPIDOMWindow::GetDocumentURI() const
3328 {
3329 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
3330 }
3332 nsIURI*
3333 nsPIDOMWindow::GetDocBaseURI() const
3334 {
3335 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
3336 }
3338 void
3339 nsPIDOMWindow::MaybeCreateDoc()
3340 {
3341 MOZ_ASSERT(!mDoc);
3342 if (nsIDocShell* docShell = GetDocShell()) {
3343 // Note that |document| here is the same thing as our mDoc, but we
3344 // don't have to explicitly set the member variable because the docshell
3345 // has already called SetNewDocument().
3346 nsCOMPtr<nsIDocument> document = do_GetInterface(docShell);
3347 }
3348 }
3350 Element*
3351 nsPIDOMWindow::GetFrameElementInternal() const
3352 {
3353 if (mOuterWindow) {
3354 return mOuterWindow->GetFrameElementInternal();
3355 }
3357 NS_ASSERTION(!IsInnerWindow(),
3358 "GetFrameElementInternal() called on orphan inner window");
3360 return mFrameElement;
3361 }
3363 void
3364 nsPIDOMWindow::SetFrameElementInternal(Element* aFrameElement)
3365 {
3366 if (IsOuterWindow()) {
3367 mFrameElement = aFrameElement;
3369 return;
3370 }
3372 if (!mOuterWindow) {
3373 NS_ERROR("frameElement set on inner window with no outer!");
3375 return;
3376 }
3378 mOuterWindow->SetFrameElementInternal(aFrameElement);
3379 }
3381 void
3382 nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
3383 {
3384 mAudioContexts.AppendElement(aAudioContext);
3386 nsIDocShell* docShell = GetDocShell();
3387 if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) {
3388 aAudioContext->Mute();
3389 }
3390 }
3392 void
3393 nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext)
3394 {
3395 mAudioContexts.RemoveElement(aAudioContext);
3396 }
3398 void
3399 nsPIDOMWindow::MuteAudioContexts()
3400 {
3401 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
3402 if (!mAudioContexts[i]->IsOffline()) {
3403 mAudioContexts[i]->Mute();
3404 }
3405 }
3406 }
3408 void
3409 nsPIDOMWindow::UnmuteAudioContexts()
3410 {
3411 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
3412 if (!mAudioContexts[i]->IsOffline()) {
3413 mAudioContexts[i]->Unmute();
3414 }
3415 }
3416 }
3418 NS_IMETHODIMP
3419 nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument)
3420 {
3421 nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(GetDocument());
3422 document.forget(aDocument);
3423 return NS_OK;
3424 }
3426 nsIDOMWindow*
3427 nsGlobalWindow::GetWindow(ErrorResult& aError)
3428 {
3429 FORWARD_TO_OUTER_OR_THROW(GetWindow, (aError), aError, nullptr);
3431 return this;
3432 }
3434 NS_IMETHODIMP
3435 nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow)
3436 {
3437 ErrorResult rv;
3438 nsCOMPtr<nsIDOMWindow> window = GetWindow(rv);
3439 window.forget(aWindow);
3441 return rv.ErrorCode();
3442 }
3444 nsIDOMWindow*
3445 nsGlobalWindow::GetSelf(ErrorResult& aError)
3446 {
3447 FORWARD_TO_OUTER_OR_THROW(GetSelf, (aError), aError, nullptr);
3449 return this;
3450 }
3452 NS_IMETHODIMP
3453 nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow)
3454 {
3455 ErrorResult rv;
3456 nsCOMPtr<nsIDOMWindow> window = GetSelf(rv);
3457 window.forget(aWindow);
3459 return rv.ErrorCode();
3460 }
3462 Navigator*
3463 nsGlobalWindow::GetNavigator(ErrorResult& aError)
3464 {
3465 FORWARD_TO_INNER_OR_THROW(GetNavigator, (aError), aError, nullptr);
3467 if (!mNavigator) {
3468 mNavigator = new Navigator(this);
3469 }
3471 return mNavigator;
3472 }
3474 NS_IMETHODIMP
3475 nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator)
3476 {
3477 ErrorResult rv;
3478 nsCOMPtr<nsIDOMNavigator> navigator = GetNavigator(rv);
3479 navigator.forget(aNavigator);
3481 return rv.ErrorCode();
3482 }
3484 nsScreen*
3485 nsGlobalWindow::GetScreen(ErrorResult& aError)
3486 {
3487 FORWARD_TO_INNER_OR_THROW(GetScreen, (aError), aError, nullptr);
3489 if (!mScreen) {
3490 mScreen = nsScreen::Create(this);
3491 if (!mScreen) {
3492 aError.Throw(NS_ERROR_UNEXPECTED);
3493 return nullptr;
3494 }
3495 }
3497 return mScreen;
3498 }
3500 NS_IMETHODIMP
3501 nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen)
3502 {
3503 ErrorResult rv;
3504 nsRefPtr<nsScreen> screen = GetScreen(rv);
3505 screen.forget(aScreen);
3507 return rv.ErrorCode();
3508 }
3510 nsHistory*
3511 nsGlobalWindow::GetHistory(ErrorResult& aError)
3512 {
3513 FORWARD_TO_INNER_OR_THROW(GetHistory, (aError), aError, nullptr);
3515 if (!mHistory) {
3516 mHistory = new nsHistory(this);
3517 }
3519 return mHistory;
3520 }
3522 NS_IMETHODIMP
3523 nsGlobalWindow::GetHistory(nsISupports** aHistory)
3524 {
3525 ErrorResult rv;
3526 nsCOMPtr<nsISupports> history = GetHistory(rv);
3527 history.forget(aHistory);
3529 return rv.ErrorCode();
3530 }
3532 nsPerformance*
3533 nsGlobalWindow::GetPerformance(ErrorResult& aError)
3534 {
3535 FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr);
3537 nsPerformance* p = nsPIDOMWindow::GetPerformance();
3538 if (!p) {
3539 aError.Throw(NS_ERROR_FAILURE);
3540 }
3541 return p;
3542 }
3544 NS_IMETHODIMP
3545 nsGlobalWindow::GetPerformance(nsISupports** aPerformance)
3546 {
3547 ErrorResult rv;
3548 nsCOMPtr<nsISupports> performance = GetPerformance(rv);
3549 performance.forget(aPerformance);
3551 return rv.ErrorCode();
3552 }
3554 nsPerformance*
3555 nsPIDOMWindow::GetPerformance()
3556 {
3557 MOZ_ASSERT(IsInnerWindow());
3558 CreatePerformanceObjectIfNeeded();
3559 return mPerformance;
3560 }
3562 void
3563 nsPIDOMWindow::CreatePerformanceObjectIfNeeded()
3564 {
3565 if (mPerformance || !mDoc) {
3566 return;
3567 }
3568 nsRefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
3569 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
3570 bool timingEnabled = false;
3571 if (!timedChannel ||
3572 !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
3573 !timingEnabled) {
3574 timedChannel = nullptr;
3575 }
3576 if (timing) {
3577 // If we are dealing with an iframe, we will need the parent's performance
3578 // object (so we can add the iframe as a resource of that page).
3579 nsPerformance* parentPerformance = nullptr;
3580 nsCOMPtr<nsIDOMWindow> parentWindow;
3581 GetScriptableParent(getter_AddRefs(parentWindow));
3582 nsCOMPtr<nsPIDOMWindow> parentPWindow = do_GetInterface(parentWindow);
3583 if (GetOuterWindow() != parentPWindow) {
3584 if (parentPWindow && !parentPWindow->IsInnerWindow()) {
3585 parentPWindow = parentPWindow->GetCurrentInnerWindow();
3586 }
3587 if (parentPWindow) {
3588 parentPerformance = parentPWindow->GetPerformance();
3589 }
3590 }
3591 mPerformance =
3592 new nsPerformance(this, timing, timedChannel, parentPerformance);
3593 }
3594 }
3596 bool
3597 nsPIDOMWindow::GetAudioMuted() const
3598 {
3599 if (!IsInnerWindow()) {
3600 return mInnerWindow->GetAudioMuted();
3601 }
3603 return mAudioMuted;
3604 }
3606 void
3607 nsPIDOMWindow::SetAudioMuted(bool aMuted)
3608 {
3609 if (!IsInnerWindow()) {
3610 mInnerWindow->SetAudioMuted(aMuted);
3611 return;
3612 }
3614 if (mAudioMuted == aMuted) {
3615 return;
3616 }
3618 mAudioMuted = aMuted;
3619 RefreshMediaElements();
3620 }
3622 float
3623 nsPIDOMWindow::GetAudioVolume() const
3624 {
3625 if (!IsInnerWindow()) {
3626 return mInnerWindow->GetAudioVolume();
3627 }
3629 return mAudioVolume;
3630 }
3632 nsresult
3633 nsPIDOMWindow::SetAudioVolume(float aVolume)
3634 {
3635 if (!IsInnerWindow()) {
3636 return mInnerWindow->SetAudioVolume(aVolume);
3637 }
3639 if (aVolume < 0.0) {
3640 return NS_ERROR_DOM_INDEX_SIZE_ERR;
3641 }
3643 if (mAudioVolume == aVolume) {
3644 return NS_OK;
3645 }
3647 mAudioVolume = aVolume;
3648 RefreshMediaElements();
3649 return NS_OK;
3650 }
3652 float
3653 nsPIDOMWindow::GetAudioGlobalVolume()
3654 {
3655 float globalVolume = 1.0;
3656 nsCOMPtr<nsPIDOMWindow> window = this;
3658 do {
3659 if (window->GetAudioMuted()) {
3660 return 0;
3661 }
3663 globalVolume *= window->GetAudioVolume();
3665 nsCOMPtr<nsIDOMWindow> win;
3666 window->GetParent(getter_AddRefs(win));
3667 if (window == win) {
3668 break;
3669 }
3671 window = do_QueryInterface(win);
3673 // If there is not parent, or we are the toplevel or the volume is
3674 // already 0.0, we don't continue.
3675 } while (window && window != this && globalVolume);
3677 return globalVolume;
3678 }
3680 void
3681 nsPIDOMWindow::RefreshMediaElements()
3682 {
3683 nsRefPtr<AudioChannelService> service =
3684 AudioChannelService::GetAudioChannelService();
3685 if (service) {
3686 service->RefreshAgentsVolume(this);
3687 }
3688 }
3690 // nsISpeechSynthesisGetter
3692 #ifdef MOZ_WEBSPEECH
3693 SpeechSynthesis*
3694 nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
3695 {
3696 FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr);
3698 if (!mSpeechSynthesis) {
3699 mSpeechSynthesis = new SpeechSynthesis(this);
3700 }
3702 return mSpeechSynthesis;
3703 }
3705 NS_IMETHODIMP
3706 nsGlobalWindow::GetSpeechSynthesis(nsISupports** aSpeechSynthesis)
3707 {
3708 ErrorResult rv;
3709 nsCOMPtr<nsISupports> speechSynthesis;
3710 if (Preferences::GetBool("media.webspeech.synth.enabled")) {
3711 speechSynthesis = GetSpeechSynthesis(rv);
3712 }
3713 speechSynthesis.forget(aSpeechSynthesis);
3715 return rv.ErrorCode();
3716 }
3717 #endif
3719 already_AddRefed<nsIDOMWindow>
3720 nsGlobalWindow::GetParent(ErrorResult& aError)
3721 {
3722 FORWARD_TO_OUTER_OR_THROW(GetParent, (aError), aError, nullptr);
3724 if (!mDocShell) {
3725 return nullptr;
3726 }
3728 nsCOMPtr<nsIDOMWindow> parent;
3729 if (mDocShell->GetIsBrowserOrApp()) {
3730 parent = this;
3731 } else {
3732 aError = GetRealParent(getter_AddRefs(parent));
3733 }
3735 return parent.forget();
3736 }
3738 /**
3739 * GetScriptableParent is called when script reads window.parent.
3740 *
3741 * In contrast to GetRealParent, GetScriptableParent respects <iframe
3742 * mozbrowser> boundaries, so if |this| is contained by an <iframe
3743 * mozbrowser>, we will return |this| as its own parent.
3744 */
3745 NS_IMETHODIMP
3746 nsGlobalWindow::GetScriptableParent(nsIDOMWindow** aParent)
3747 {
3748 ErrorResult rv;
3749 nsCOMPtr<nsIDOMWindow> parent = GetParent(rv);
3750 parent.forget(aParent);
3752 return rv.ErrorCode();
3753 }
3755 /**
3756 * nsIDOMWindow::GetParent (when called from C++) is just a wrapper around
3757 * GetRealParent.
3758 */
3759 NS_IMETHODIMP
3760 nsGlobalWindow::GetRealParent(nsIDOMWindow** aParent)
3761 {
3762 FORWARD_TO_OUTER(GetRealParent, (aParent), NS_ERROR_NOT_INITIALIZED);
3764 *aParent = nullptr;
3765 if (!mDocShell) {
3766 return NS_OK;
3767 }
3769 nsCOMPtr<nsIDocShell> parent;
3770 mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
3772 if (parent) {
3773 nsCOMPtr<nsIScriptGlobalObject> globalObject(do_GetInterface(parent));
3774 NS_ENSURE_SUCCESS(CallQueryInterface(globalObject.get(), aParent),
3775 NS_ERROR_FAILURE);
3776 }
3777 else {
3778 *aParent = static_cast<nsIDOMWindow*>(this);
3779 NS_ADDREF(*aParent);
3780 }
3781 return NS_OK;
3782 }
3784 static nsresult
3785 GetTopImpl(nsGlobalWindow* aWin, nsIDOMWindow** aTop, bool aScriptable)
3786 {
3787 *aTop = nullptr;
3789 // Walk up the parent chain.
3791 nsCOMPtr<nsIDOMWindow> prevParent = aWin;
3792 nsCOMPtr<nsIDOMWindow> parent = aWin;
3793 do {
3794 if (!parent) {
3795 break;
3796 }
3798 prevParent = parent;
3800 nsCOMPtr<nsIDOMWindow> newParent;
3801 nsresult rv;
3802 if (aScriptable) {
3803 rv = parent->GetScriptableParent(getter_AddRefs(newParent));
3804 }
3805 else {
3806 rv = parent->GetParent(getter_AddRefs(newParent));
3807 }
3808 NS_ENSURE_SUCCESS(rv, rv);
3810 parent = newParent;
3812 } while (parent != prevParent);
3814 if (parent) {
3815 parent.swap(*aTop);
3816 }
3818 return NS_OK;
3819 }
3821 /**
3822 * GetScriptableTop is called when script reads window.top.
3823 *
3824 * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser>
3825 * boundaries. If we encounter a window owned by an <iframe mozbrowser> while
3826 * walking up the window hierarchy, we'll stop and return that window.
3827 */
3828 NS_IMETHODIMP
3829 nsGlobalWindow::GetScriptableTop(nsIDOMWindow **aTop)
3830 {
3831 FORWARD_TO_OUTER(GetScriptableTop, (aTop), NS_ERROR_NOT_INITIALIZED);
3832 return GetTopImpl(this, aTop, /* aScriptable = */ true);
3833 }
3835 /**
3836 * nsIDOMWindow::GetTop (when called from C++) is just a wrapper around
3837 * GetRealTop.
3838 */
3839 NS_IMETHODIMP
3840 nsGlobalWindow::GetRealTop(nsIDOMWindow** aTop)
3841 {
3842 nsGlobalWindow* outer;
3843 if (IsInnerWindow()) {
3844 outer = GetOuterWindowInternal();
3845 if (!outer) {
3846 NS_WARNING("No outer window available!");
3847 return NS_ERROR_NOT_INITIALIZED;
3848 }
3849 } else {
3850 outer = this;
3851 }
3852 return GetTopImpl(outer, aTop, /* aScriptable = */ false);
3853 }
3855 void
3856 nsGlobalWindow::GetContent(JSContext* aCx,
3857 JS::MutableHandle<JSObject*> aRetval,
3858 ErrorResult& aError)
3859 {
3860 FORWARD_TO_OUTER_OR_THROW(GetContent, (aCx, aRetval, aError), aError, );
3862 nsCOMPtr<nsIDOMWindow> content = GetContentInternal(aError);
3863 if (aError.Failed()) {
3864 return;
3865 }
3867 if (content) {
3868 JS::Rooted<JS::Value> val(aCx);
3869 aError = nsContentUtils::WrapNative(aCx, content, &val);
3870 if (aError.Failed()) {
3871 return;
3872 }
3874 aRetval.set(&val.toObject());
3875 return;
3876 }
3878 if (!nsContentUtils::IsCallerChrome() || !IsChromeWindow()) {
3879 aError.Throw(NS_ERROR_FAILURE);
3880 return;
3881 }
3883 // Something tries to get .content on a ChromeWindow, try to fetch the CPOW.
3884 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
3885 if (!treeOwner) {
3886 aError.Throw(NS_ERROR_FAILURE);
3887 return;
3888 }
3890 JS::Rooted<JS::Value> val(aCx, JS::NullValue());
3891 aError = treeOwner->GetContentWindow(aCx, &val);
3892 if (aError.Failed()) {
3893 return;
3894 }
3896 aRetval.set(val.toObjectOrNull());
3897 }
3899 already_AddRefed<nsIDOMWindow>
3900 nsGlobalWindow::GetContentInternal(ErrorResult& aError)
3901 {
3902 // First check for a named frame named "content"
3903 nsCOMPtr<nsIDOMWindow> domWindow =
3904 GetChildWindow(NS_LITERAL_STRING("content"));
3905 if (domWindow) {
3906 return domWindow.forget();
3907 }
3909 // If we're contained in <iframe mozbrowser> or <iframe mozapp>, then
3910 // GetContent is the same as window.top.
3911 if (mDocShell && mDocShell->GetIsInBrowserOrApp()) {
3912 return GetTop(aError);
3913 }
3915 nsCOMPtr<nsIDocShellTreeItem> primaryContent;
3916 if (!nsContentUtils::IsCallerChrome()) {
3917 // If we're called by non-chrome code, make sure we don't return
3918 // the primary content window if the calling tab is hidden. In
3919 // such a case we return the same-type root in the hidden tab,
3920 // which is "good enough", for now.
3921 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell));
3923 if (baseWin) {
3924 bool visible = false;
3925 baseWin->GetVisibility(&visible);
3927 if (!visible) {
3928 mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(primaryContent));
3929 }
3930 }
3931 }
3933 if (!primaryContent) {
3934 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
3935 if (!treeOwner) {
3936 aError.Throw(NS_ERROR_FAILURE);
3937 return nullptr;
3938 }
3940 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
3941 }
3943 domWindow = do_GetInterface(primaryContent);
3944 return domWindow.forget();
3945 }
3947 NS_IMETHODIMP
3948 nsGlobalWindow::GetContent(nsIDOMWindow** aContent)
3949 {
3950 ErrorResult rv;
3951 *aContent = GetContentInternal(rv).take();
3953 return rv.ErrorCode();
3954 }
3956 NS_IMETHODIMP
3957 nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
3958 {
3959 ErrorResult rv;
3960 JS::Rooted<JSObject*> content(aCx);
3961 GetContent(aCx, &content, rv);
3962 if (!rv.Failed()) {
3963 aVal.setObjectOrNull(content);
3964 }
3966 return rv.ErrorCode();
3967 }
3969 NS_IMETHODIMP
3970 nsGlobalWindow::GetPrompter(nsIPrompt** aPrompt)
3971 {
3972 if (IsInnerWindow()) {
3973 nsGlobalWindow* outer = GetOuterWindowInternal();
3974 if (!outer) {
3975 NS_WARNING("No outer window available!");
3976 return NS_ERROR_NOT_INITIALIZED;
3977 }
3978 return outer->GetPrompter(aPrompt);
3979 }
3981 if (!mDocShell)
3982 return NS_ERROR_FAILURE;
3984 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
3985 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
3987 NS_ADDREF(*aPrompt = prompter);
3988 return NS_OK;
3989 }
3991 BarProp*
3992 nsGlobalWindow::GetMenubar(ErrorResult& aError)
3993 {
3994 FORWARD_TO_INNER_OR_THROW(GetMenubar, (aError), aError, nullptr);
3996 if (!mMenubar) {
3997 mMenubar = new MenubarProp(this);
3998 }
4000 return mMenubar;
4001 }
4003 NS_IMETHODIMP
4004 nsGlobalWindow::GetMenubar(nsISupports** aMenubar)
4005 {
4006 ErrorResult rv;
4007 nsCOMPtr<nsISupports> menubar = GetMenubar(rv);
4008 menubar.forget(aMenubar);
4010 return rv.ErrorCode();
4011 }
4013 BarProp*
4014 nsGlobalWindow::GetToolbar(ErrorResult& aError)
4015 {
4016 FORWARD_TO_INNER_OR_THROW(GetToolbar, (aError), aError, nullptr);
4018 if (!mToolbar) {
4019 mToolbar = new ToolbarProp(this);
4020 }
4022 return mToolbar;
4023 }
4025 NS_IMETHODIMP
4026 nsGlobalWindow::GetToolbar(nsISupports** aToolbar)
4027 {
4028 ErrorResult rv;
4029 nsCOMPtr<nsISupports> toolbar = GetToolbar(rv);
4030 toolbar.forget(aToolbar);
4032 return rv.ErrorCode();
4033 }
4035 BarProp*
4036 nsGlobalWindow::GetLocationbar(ErrorResult& aError)
4037 {
4038 FORWARD_TO_INNER_OR_THROW(GetLocationbar, (aError), aError, nullptr);
4040 if (!mLocationbar) {
4041 mLocationbar = new LocationbarProp(this);
4042 }
4043 return mLocationbar;
4044 }
4046 NS_IMETHODIMP
4047 nsGlobalWindow::GetLocationbar(nsISupports** aLocationbar)
4048 {
4049 ErrorResult rv;
4050 nsCOMPtr<nsISupports> locationbar = GetLocationbar(rv);
4051 locationbar.forget(aLocationbar);
4053 return rv.ErrorCode();
4054 }
4056 BarProp*
4057 nsGlobalWindow::GetPersonalbar(ErrorResult& aError)
4058 {
4059 FORWARD_TO_INNER_OR_THROW(GetPersonalbar, (aError), aError, nullptr);
4061 if (!mPersonalbar) {
4062 mPersonalbar = new PersonalbarProp(this);
4063 }
4064 return mPersonalbar;
4065 }
4067 NS_IMETHODIMP
4068 nsGlobalWindow::GetPersonalbar(nsISupports** aPersonalbar)
4069 {
4070 ErrorResult rv;
4071 nsCOMPtr<nsISupports> personalbar = GetPersonalbar(rv);
4072 personalbar.forget(aPersonalbar);
4074 return rv.ErrorCode();
4075 }
4077 BarProp*
4078 nsGlobalWindow::GetStatusbar(ErrorResult& aError)
4079 {
4080 FORWARD_TO_INNER_OR_THROW(GetStatusbar, (aError), aError, nullptr);
4082 if (!mStatusbar) {
4083 mStatusbar = new StatusbarProp(this);
4084 }
4085 return mStatusbar;
4086 }
4088 NS_IMETHODIMP
4089 nsGlobalWindow::GetStatusbar(nsISupports** aStatusbar)
4090 {
4091 ErrorResult rv;
4092 nsCOMPtr<nsISupports> statusbar = GetStatusbar(rv);
4093 statusbar.forget(aStatusbar);
4095 return rv.ErrorCode();
4096 }
4098 BarProp*
4099 nsGlobalWindow::GetScrollbars(ErrorResult& aError)
4100 {
4101 FORWARD_TO_INNER_OR_THROW(GetScrollbars, (aError), aError, nullptr);
4103 if (!mScrollbars) {
4104 mScrollbars = new ScrollbarsProp(this);
4105 }
4107 return mScrollbars;
4108 }
4110 NS_IMETHODIMP
4111 nsGlobalWindow::GetScrollbars(nsISupports** aScrollbars)
4112 {
4113 ErrorResult rv;
4114 nsCOMPtr<nsISupports> scrollbars = GetScrollbars(rv);
4115 scrollbars.forget(aScrollbars);
4117 return rv.ErrorCode();
4118 }
4120 bool
4121 nsGlobalWindow::GetClosed(ErrorResult& aError)
4122 {
4123 FORWARD_TO_OUTER_OR_THROW(GetClosed, (aError), aError, false);
4125 // If someone called close(), or if we don't have a docshell, we're closed.
4126 return mIsClosed || !mDocShell;
4127 }
4129 NS_IMETHODIMP
4130 nsGlobalWindow::GetClosed(bool* aClosed)
4131 {
4132 ErrorResult rv;
4133 *aClosed = GetClosed(rv);
4135 return rv.ErrorCode();
4136 }
4138 nsDOMWindowList*
4139 nsGlobalWindow::GetWindowList()
4140 {
4141 MOZ_ASSERT(IsOuterWindow());
4143 if (!mFrames && mDocShell) {
4144 mFrames = new nsDOMWindowList(mDocShell);
4145 }
4147 return mFrames;
4148 }
4150 NS_IMETHODIMP
4151 nsGlobalWindow::GetFrames(nsIDOMWindowCollection** aFrames)
4152 {
4153 FORWARD_TO_OUTER(GetFrames, (aFrames), NS_ERROR_NOT_INITIALIZED);
4155 *aFrames = GetWindowList();
4156 NS_IF_ADDREF(*aFrames);
4157 return NS_OK;
4158 }
4160 already_AddRefed<nsIDOMWindow>
4161 nsGlobalWindow::IndexedGetter(uint32_t aIndex, bool& aFound)
4162 {
4163 aFound = false;
4165 FORWARD_TO_OUTER(IndexedGetter, (aIndex, aFound), nullptr);
4167 nsDOMWindowList* windows = GetWindowList();
4168 NS_ENSURE_TRUE(windows, nullptr);
4170 return windows->IndexedGetter(aIndex, aFound);
4171 }
4173 void
4174 nsGlobalWindow::GetSupportedNames(nsTArray<nsString>& aNames)
4175 {
4176 FORWARD_TO_OUTER_VOID(GetSupportedNames, (aNames));
4178 nsDOMWindowList* windows = GetWindowList();
4179 if (windows) {
4180 uint32_t length = windows->GetLength();
4181 nsString* name = aNames.AppendElements(length);
4182 for (uint32_t i = 0; i < length; ++i, ++name) {
4183 nsCOMPtr<nsIDocShellTreeItem> item =
4184 windows->GetDocShellTreeItemAt(i);
4185 item->GetName(*name);
4186 }
4187 }
4188 }
4190 bool
4191 nsGlobalWindow::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
4192 JS::Handle<jsid> aId,
4193 JS::MutableHandle<JSPropertyDescriptor> aDesc)
4194 {
4195 MOZ_ASSERT(IsInnerWindow());
4197 if (!JSID_IS_STRING(aId)) {
4198 return true;
4199 }
4201 nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc);
4202 if (NS_FAILED(rv)) {
4203 return Throw(aCx, rv);
4204 }
4206 return true;
4207 }
4209 struct GlobalNameEnumeratorClosure
4210 {
4211 GlobalNameEnumeratorClosure(JSContext* aCx, nsGlobalWindow* aWindow,
4212 nsTArray<nsString>& aNames)
4213 : mCx(aCx),
4214 mWindow(aWindow),
4215 mWrapper(aCx, aWindow->GetWrapper()),
4216 mNames(aNames)
4217 {
4218 }
4220 JSContext* mCx;
4221 nsGlobalWindow* mWindow;
4222 JS::Rooted<JSObject*> mWrapper;
4223 nsTArray<nsString>& mNames;
4224 };
4226 static PLDHashOperator
4227 EnumerateGlobalName(const nsAString& aName,
4228 const nsGlobalNameStruct& aNameStruct,
4229 void* aClosure)
4230 {
4231 GlobalNameEnumeratorClosure* closure =
4232 static_cast<GlobalNameEnumeratorClosure*>(aClosure);
4234 if (aNameStruct.mType == nsGlobalNameStruct::eTypeStaticNameSet) {
4235 // We have no idea what names this might install.
4236 return PL_DHASH_NEXT;
4237 }
4239 if (nsWindowSH::NameStructEnabled(closure->mCx, closure->mWindow, aName,
4240 aNameStruct) &&
4241 (!aNameStruct.mConstructorEnabled ||
4242 aNameStruct.mConstructorEnabled(closure->mCx, closure->mWrapper))) {
4243 closure->mNames.AppendElement(aName);
4244 }
4245 return PL_DHASH_NEXT;
4246 }
4248 void
4249 nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
4250 ErrorResult& aRv)
4251 {
4252 // "Components" is marked as enumerable but only resolved on demand :-/.
4253 //aNames.AppendElement(NS_LITERAL_STRING("Components"));
4255 nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
4256 if (nameSpaceManager) {
4257 GlobalNameEnumeratorClosure closure(aCx, this, aNames);
4258 nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &closure);
4259 }
4260 }
4262 /* static */ bool
4263 nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
4264 {
4265 // For now, have to deal with XPConnect objects here.
4266 return xpc::WindowOrNull(aObj)->IsChromeWindow();
4267 }
4269 nsIDOMOfflineResourceList*
4270 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
4271 {
4272 FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr);
4274 if (!mApplicationCache) {
4275 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
4276 if (!webNav) {
4277 aError.Throw(NS_ERROR_FAILURE);
4278 return nullptr;
4279 }
4281 nsCOMPtr<nsIURI> uri;
4282 aError = webNav->GetCurrentURI(getter_AddRefs(uri));
4283 if (aError.Failed()) {
4284 return nullptr;
4285 }
4287 nsCOMPtr<nsIURI> manifestURI;
4288 nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));
4290 nsRefPtr<nsDOMOfflineResourceList> applicationCache =
4291 new nsDOMOfflineResourceList(manifestURI, uri, this);
4293 applicationCache->Init();
4295 mApplicationCache = applicationCache;
4296 }
4298 return mApplicationCache;
4299 }
4301 NS_IMETHODIMP
4302 nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCache)
4303 {
4304 ErrorResult rv;
4305 nsCOMPtr<nsIDOMOfflineResourceList> applicationCache =
4306 GetApplicationCache(rv);
4307 applicationCache.forget(aApplicationCache);
4309 return rv.ErrorCode();
4310 }
4312 nsIDOMCrypto*
4313 nsGlobalWindow::GetCrypto(ErrorResult& aError)
4314 {
4315 FORWARD_TO_INNER_OR_THROW(GetCrypto, (aError), aError, nullptr);
4317 if (!mCrypto) {
4318 #ifndef MOZ_DISABLE_CRYPTOLEGACY
4319 if (XRE_GetProcessType() != GeckoProcessType_Content) {
4320 nsresult rv;
4321 mCrypto = do_CreateInstance(NS_CRYPTO_CONTRACTID, &rv);
4322 if (NS_FAILED(rv)) {
4323 aError.Throw(rv);
4324 return nullptr;
4325 }
4326 } else
4327 #endif
4328 {
4329 mCrypto = new Crypto();
4330 }
4332 mCrypto->Init(this);
4333 }
4334 return mCrypto;
4335 }
4337 NS_IMETHODIMP
4338 nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto)
4339 {
4340 ErrorResult rv;
4341 nsCOMPtr<nsIDOMCrypto> crypto = GetCrypto(rv);
4342 crypto.forget(aCrypto);
4344 return rv.ErrorCode();
4345 }
4347 nsIControllers*
4348 nsGlobalWindow::GetControllers(ErrorResult& aError)
4349 {
4350 FORWARD_TO_OUTER_OR_THROW(GetControllers, (aError), aError, nullptr);
4352 if (!mControllers) {
4353 nsresult rv;
4354 mControllers = do_CreateInstance(kXULControllersCID, &rv);
4355 if (NS_FAILED(rv)) {
4356 aError.Throw(rv);
4357 return nullptr;
4358 }
4360 // Add in the default controller
4361 nsCOMPtr<nsIController> controller = do_CreateInstance(
4362 NS_WINDOWCONTROLLER_CONTRACTID, &rv);
4363 if (NS_FAILED(rv)) {
4364 aError.Throw(rv);
4365 return nullptr;
4366 }
4368 mControllers->InsertControllerAt(0, controller);
4369 nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller);
4370 if (!controllerContext) {
4371 aError.Throw(NS_ERROR_FAILURE);
4372 return nullptr;
4373 }
4375 controllerContext->SetCommandContext(static_cast<nsIDOMWindow*>(this));
4376 }
4378 return mControllers;
4379 }
4381 NS_IMETHODIMP
4382 nsGlobalWindow::GetControllers(nsIControllers** aResult)
4383 {
4384 ErrorResult rv;
4385 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
4386 controllers.forget(aResult);
4388 return rv.ErrorCode();
4389 }
4391 nsIDOMWindow*
4392 nsGlobalWindow::GetOpenerWindow(ErrorResult& aError)
4393 {
4394 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindow, (aError), aError, nullptr);
4396 nsCOMPtr<nsPIDOMWindow> opener = do_QueryReferent(mOpener);
4397 if (!opener) {
4398 return nullptr;
4399 }
4401 // First, check if we were called from a privileged chrome script
4402 if (nsContentUtils::IsCallerChrome()) {
4403 return opener;
4404 }
4406 // First, ensure that we're not handing back a chrome window.
4407 nsGlobalWindow *win = static_cast<nsGlobalWindow *>(opener.get());
4408 if (win->IsChromeWindow()) {
4409 return nullptr;
4410 }
4412 // We don't want to reveal the opener if the opener is a mail window,
4413 // because opener can be used to spoof the contents of a message (bug 105050).
4414 // So, we look in the opener's root docshell to see if it's a mail window.
4415 nsCOMPtr<nsIDocShell> openerDocShell = opener->GetDocShell();
4417 if (openerDocShell) {
4418 nsCOMPtr<nsIDocShellTreeItem> openerRootItem;
4419 openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem));
4420 nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem));
4421 if (openerRootDocShell) {
4422 uint32_t appType;
4423 nsresult rv = openerRootDocShell->GetAppType(&appType);
4424 if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) {
4425 return opener;
4426 }
4427 }
4428 }
4430 return nullptr;
4431 }
4433 void
4434 nsGlobalWindow::GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
4435 ErrorResult& aError)
4436 {
4437 nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(aError);
4438 if (aError.Failed() || !opener) {
4439 aRetval.setNull();
4440 return;
4441 }
4443 aError = nsContentUtils::WrapNative(aCx, opener, aRetval);
4444 }
4446 NS_IMETHODIMP
4447 nsGlobalWindow::GetScriptableOpener(JSContext* aCx,
4448 JS::MutableHandle<JS::Value> aOpener)
4449 {
4450 ErrorResult rv;
4451 GetOpener(aCx, aOpener, rv);
4453 return rv.ErrorCode();
4454 }
4456 NS_IMETHODIMP
4457 nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener)
4458 {
4459 ErrorResult rv;
4460 nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(rv);
4461 opener.forget(aOpener);
4462 return rv.ErrorCode();
4463 }
4465 void
4466 nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
4467 ErrorResult& aError)
4468 {
4469 // Check if we were called from a privileged chrome script. If not, and if
4470 // aOpener is not null, just define aOpener on our inner window's JS object,
4471 // wrapped into the current compartment so that for Xrays we define on the
4472 // Xray expando object, but don't set it on the outer window, so that it'll
4473 // get reset on navigation. This is just like replaceable properties, but
4474 // we're not quite readonly.
4475 if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
4476 JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
4477 if (!thisObj) {
4478 aError.Throw(NS_ERROR_UNEXPECTED);
4479 return;
4480 }
4482 if (!JS_WrapObject(aCx, &thisObj) ||
4483 !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
4484 JS_PropertyStub, JS_StrictPropertyStub)) {
4485 aError.Throw(NS_ERROR_FAILURE);
4486 }
4488 return;
4489 }
4491 if (!aOpener.isObjectOrNull()) {
4492 // Chrome code trying to set some random value as opener
4493 aError.Throw(NS_ERROR_INVALID_ARG);
4494 return;
4495 }
4497 nsGlobalWindow* win = nullptr;
4498 if (aOpener.isObject()) {
4499 JSObject* unwrapped = js::CheckedUnwrap(&aOpener.toObject(),
4500 /* stopAtOuter = */ false);
4501 if (!unwrapped) {
4502 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
4503 return;
4504 }
4506 win = xpc::WindowOrNull(unwrapped);
4507 if (!win) {
4508 // Wasn't a window
4509 aError.Throw(NS_ERROR_INVALID_ARG);
4510 return;
4511 }
4512 }
4514 SetOpenerWindow(win, false);
4515 }
4517 NS_IMETHODIMP
4518 nsGlobalWindow::SetScriptableOpener(JSContext* aCx,
4519 JS::Handle<JS::Value> aOpener)
4520 {
4521 ErrorResult rv;
4522 SetOpener(aCx, aOpener, rv);
4524 return rv.ErrorCode();
4525 }
4527 NS_IMETHODIMP
4528 nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener)
4529 {
4530 SetOpenerWindow(aOpener, false);
4531 return NS_OK;
4532 }
4534 void
4535 nsGlobalWindow::GetStatus(nsAString& aStatus, ErrorResult& aError)
4536 {
4537 FORWARD_TO_OUTER_OR_THROW(GetStatus, (aStatus, aError), aError, );
4539 aStatus = mStatus;
4540 }
4542 NS_IMETHODIMP
4543 nsGlobalWindow::GetStatus(nsAString& aStatus)
4544 {
4545 ErrorResult rv;
4546 GetStatus(aStatus, rv);
4548 return rv.ErrorCode();
4549 }
4551 void
4552 nsGlobalWindow::SetStatus(const nsAString& aStatus, ErrorResult& aError)
4553 {
4554 FORWARD_TO_OUTER_OR_THROW(SetStatus, (aStatus, aError), aError, );
4556 mStatus = aStatus;
4558 /*
4559 * If caller is not chrome and dom.disable_window_status_change is true,
4560 * prevent propagating window.status to the UI by exiting early
4561 */
4563 if (!CanSetProperty("dom.disable_window_status_change")) {
4564 return;
4565 }
4567 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
4568 if (browserChrome) {
4569 browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
4570 PromiseFlatString(aStatus).get());
4571 }
4572 }
4574 NS_IMETHODIMP
4575 nsGlobalWindow::SetStatus(const nsAString& aStatus)
4576 {
4577 ErrorResult rv;
4578 SetStatus(aStatus, rv);
4580 return rv.ErrorCode();
4581 }
4583 void
4584 nsGlobalWindow::GetName(nsAString& aName, ErrorResult& aError)
4585 {
4586 FORWARD_TO_OUTER_OR_THROW(GetName, (aName, aError), aError, );
4588 if (mDocShell) {
4589 mDocShell->GetName(aName);
4590 }
4591 }
4593 NS_IMETHODIMP
4594 nsGlobalWindow::GetName(nsAString& aName)
4595 {
4596 ErrorResult rv;
4597 GetName(aName, rv);
4599 return rv.ErrorCode();
4600 }
4602 void
4603 nsGlobalWindow::SetName(const nsAString& aName, mozilla::ErrorResult& aError)
4604 {
4605 FORWARD_TO_OUTER_OR_THROW(SetName, (aName, aError), aError, );
4607 if (mDocShell) {
4608 aError = mDocShell->SetName(aName);
4609 }
4610 }
4612 NS_IMETHODIMP
4613 nsGlobalWindow::SetName(const nsAString& aName)
4614 {
4615 ErrorResult rv;
4616 SetName(aName, rv);
4618 return rv.ErrorCode();
4619 }
4621 // Helper functions used by many methods below.
4622 int32_t
4623 nsGlobalWindow::DevToCSSIntPixels(int32_t px)
4624 {
4625 if (!mDocShell)
4626 return px; // assume 1:1
4628 nsRefPtr<nsPresContext> presContext;
4629 mDocShell->GetPresContext(getter_AddRefs(presContext));
4630 if (!presContext)
4631 return px;
4633 return presContext->DevPixelsToIntCSSPixels(px);
4634 }
4636 int32_t
4637 nsGlobalWindow::CSSToDevIntPixels(int32_t px)
4638 {
4639 if (!mDocShell)
4640 return px; // assume 1:1
4642 nsRefPtr<nsPresContext> presContext;
4643 mDocShell->GetPresContext(getter_AddRefs(presContext));
4644 if (!presContext)
4645 return px;
4647 return presContext->CSSPixelsToDevPixels(px);
4648 }
4650 nsIntSize
4651 nsGlobalWindow::DevToCSSIntPixels(nsIntSize px)
4652 {
4653 if (!mDocShell)
4654 return px; // assume 1:1
4656 nsRefPtr<nsPresContext> presContext;
4657 mDocShell->GetPresContext(getter_AddRefs(presContext));
4658 if (!presContext)
4659 return px;
4661 return nsIntSize(
4662 presContext->DevPixelsToIntCSSPixels(px.width),
4663 presContext->DevPixelsToIntCSSPixels(px.height));
4664 }
4666 nsIntSize
4667 nsGlobalWindow::CSSToDevIntPixels(nsIntSize px)
4668 {
4669 if (!mDocShell)
4670 return px; // assume 1:1
4672 nsRefPtr<nsPresContext> presContext;
4673 mDocShell->GetPresContext(getter_AddRefs(presContext));
4674 if (!presContext)
4675 return px;
4677 return nsIntSize(
4678 presContext->CSSPixelsToDevPixels(px.width),
4679 presContext->CSSPixelsToDevPixels(px.height));
4680 }
4682 nsresult
4683 nsGlobalWindow::GetInnerSize(CSSIntSize& aSize)
4684 {
4685 MOZ_ASSERT(IsOuterWindow());
4687 EnsureSizeUpToDate();
4689 NS_ENSURE_STATE(mDocShell);
4691 nsRefPtr<nsPresContext> presContext;
4692 mDocShell->GetPresContext(getter_AddRefs(presContext));
4693 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4695 if (!presContext || !presShell) {
4696 aSize = CSSIntSize(0, 0);
4697 return NS_OK;
4698 }
4700 /*
4701 * On platforms with resolution-based zooming, the CSS viewport
4702 * and visual viewport may not be the same. The inner size should
4703 * be the visual viewport, but we fall back to the CSS viewport
4704 * if it is not set.
4705 */
4706 if (presShell->IsScrollPositionClampingScrollPortSizeSet()) {
4707 aSize = CSSIntRect::FromAppUnitsRounded(
4708 presShell->GetScrollPositionClampingScrollPortSize());
4709 } else {
4710 nsRefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4711 if (viewManager) {
4712 viewManager->FlushDelayedResize(false);
4713 }
4715 aSize = CSSIntRect::FromAppUnitsRounded(
4716 presContext->GetVisibleArea().Size());
4717 }
4718 return NS_OK;
4719 }
4721 int32_t
4722 nsGlobalWindow::GetInnerWidth(ErrorResult& aError)
4723 {
4724 FORWARD_TO_OUTER_OR_THROW(GetInnerWidth, (aError), aError, 0);
4726 CSSIntSize size;
4727 aError = GetInnerSize(size);
4728 return size.width;
4729 }
4731 NS_IMETHODIMP
4732 nsGlobalWindow::GetInnerWidth(int32_t* aInnerWidth)
4733 {
4734 ErrorResult rv;
4735 *aInnerWidth = GetInnerWidth(rv);
4737 return rv.ErrorCode();
4738 }
4740 void
4741 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth, ErrorResult& aError)
4742 {
4743 FORWARD_TO_OUTER_OR_THROW(SetInnerWidth, (aInnerWidth, aError), aError, );
4745 if (!mDocShell) {
4746 aError.Throw(NS_ERROR_UNEXPECTED);
4747 return;
4748 }
4750 /*
4751 * If caller is not chrome and the user has not explicitly exempted the site,
4752 * prevent setting window.innerWidth by exiting early
4753 */
4754 if (!CanMoveResizeWindows() || IsFrame()) {
4755 return;
4756 }
4758 CheckSecurityWidthAndHeight(&aInnerWidth, nullptr);
4760 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4762 if (presShell && presShell->GetIsViewportOverridden())
4763 {
4764 nscoord height = 0;
4766 nsRefPtr<nsPresContext> presContext;
4767 presContext = presShell->GetPresContext();
4769 nsRect shellArea = presContext->GetVisibleArea();
4770 height = shellArea.height;
4771 SetCSSViewportWidthAndHeight(nsPresContext::CSSPixelsToAppUnits(aInnerWidth),
4772 height);
4773 return;
4774 }
4776 int32_t height = 0;
4777 int32_t unused = 0;
4779 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
4780 docShellAsWin->GetSize(&unused, &height);
4781 aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height);
4782 }
4784 NS_IMETHODIMP
4785 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth)
4786 {
4787 ErrorResult rv;
4788 SetInnerWidth(aInnerWidth, rv);
4790 return rv.ErrorCode();
4791 }
4793 int32_t
4794 nsGlobalWindow::GetInnerHeight(ErrorResult& aError)
4795 {
4796 FORWARD_TO_OUTER_OR_THROW(GetInnerHeight, (aError), aError, 0);
4798 CSSIntSize size;
4799 aError = GetInnerSize(size);
4800 return size.height;
4801 }
4803 NS_IMETHODIMP
4804 nsGlobalWindow::GetInnerHeight(int32_t* aInnerHeight)
4805 {
4806 ErrorResult rv;
4807 *aInnerHeight = GetInnerHeight(rv);
4809 return rv.ErrorCode();
4810 }
4812 void
4813 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight, ErrorResult& aError)
4814 {
4815 FORWARD_TO_OUTER_OR_THROW(SetInnerHeight, (aInnerHeight, aError), aError, );
4817 if (!mDocShell) {
4818 aError.Throw(NS_ERROR_UNEXPECTED);
4819 return;
4820 }
4822 /*
4823 * If caller is not chrome and the user has not explicitly exempted the site,
4824 * prevent setting window.innerHeight by exiting early
4825 */
4826 if (!CanMoveResizeWindows() || IsFrame()) {
4827 return;
4828 }
4830 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4832 if (presShell && presShell->GetIsViewportOverridden())
4833 {
4834 nsRefPtr<nsPresContext> presContext;
4835 presContext = presShell->GetPresContext();
4837 nsRect shellArea = presContext->GetVisibleArea();
4838 nscoord height = aInnerHeight;
4839 nscoord width = shellArea.width;
4840 CheckSecurityWidthAndHeight(nullptr, &height);
4841 SetCSSViewportWidthAndHeight(width,
4842 nsPresContext::CSSPixelsToAppUnits(height));
4843 return;
4844 }
4846 int32_t height = 0;
4847 int32_t width = 0;
4849 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
4850 docShellAsWin->GetSize(&width, &height);
4851 CheckSecurityWidthAndHeight(nullptr, &aInnerHeight);
4852 aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
4853 }
4855 NS_IMETHODIMP
4856 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight)
4857 {
4858 ErrorResult rv;
4859 SetInnerHeight(aInnerHeight, rv);
4861 return rv.ErrorCode();
4862 }
4864 nsIntSize
4865 nsGlobalWindow::GetOuterSize(ErrorResult& aError)
4866 {
4867 MOZ_ASSERT(IsOuterWindow());
4869 if (!IsChrome()) {
4870 CSSIntSize size;
4871 aError = GetInnerSize(size);
4872 return nsIntSize(size.width, size.height);
4873 }
4875 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4876 if (!treeOwnerAsWin) {
4877 aError.Throw(NS_ERROR_FAILURE);
4878 return nsIntSize(0, 0);
4879 }
4881 nsGlobalWindow* rootWindow =
4882 static_cast<nsGlobalWindow *>(GetPrivateRoot());
4883 if (rootWindow) {
4884 rootWindow->FlushPendingNotifications(Flush_Layout);
4885 }
4887 nsIntSize sizeDevPixels;
4888 aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
4889 if (aError.Failed()) {
4890 return nsIntSize();
4891 }
4893 return DevToCSSIntPixels(sizeDevPixels);
4894 }
4896 int32_t
4897 nsGlobalWindow::GetOuterWidth(ErrorResult& aError)
4898 {
4899 FORWARD_TO_OUTER_OR_THROW(GetOuterWidth, (aError), aError, 0);
4900 return GetOuterSize(aError).width;
4901 }
4903 NS_IMETHODIMP
4904 nsGlobalWindow::GetOuterWidth(int32_t* aOuterWidth)
4905 {
4906 ErrorResult rv;
4907 *aOuterWidth = GetOuterWidth(rv);
4909 return rv.ErrorCode();
4910 }
4912 int32_t
4913 nsGlobalWindow::GetOuterHeight(ErrorResult& aError)
4914 {
4915 FORWARD_TO_OUTER_OR_THROW(GetOuterHeight, (aError), aError, 0);
4916 return GetOuterSize(aError).height;
4917 }
4919 NS_IMETHODIMP
4920 nsGlobalWindow::GetOuterHeight(int32_t* aOuterHeight)
4921 {
4922 ErrorResult rv;
4923 *aOuterHeight = GetOuterHeight(rv);
4925 return rv.ErrorCode();
4926 }
4928 void
4929 nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
4930 ErrorResult& aError)
4931 {
4932 MOZ_ASSERT(IsOuterWindow());
4934 /*
4935 * If caller is not chrome and the user has not explicitly exempted the site,
4936 * prevent setting window.outerWidth by exiting early
4937 */
4939 if (!CanMoveResizeWindows() || IsFrame()) {
4940 return;
4941 }
4943 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4944 if (!treeOwnerAsWin) {
4945 aError.Throw(NS_ERROR_FAILURE);
4946 return;
4947 }
4949 CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
4950 aIsWidth ? nullptr : &aLengthCSSPixels);
4952 int32_t width, height;
4953 aError = treeOwnerAsWin->GetSize(&width, &height);
4954 if (aError.Failed()) {
4955 return;
4956 }
4958 int32_t lengthDevPixels = CSSToDevIntPixels(aLengthCSSPixels);
4959 if (aIsWidth) {
4960 width = lengthDevPixels;
4961 } else {
4962 height = lengthDevPixels;
4963 }
4964 aError = treeOwnerAsWin->SetSize(width, height, true);
4965 }
4967 void
4968 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth, ErrorResult& aError)
4969 {
4970 FORWARD_TO_OUTER_OR_THROW(SetOuterWidth, (aOuterWidth, aError), aError, );
4972 SetOuterSize(aOuterWidth, true, aError);
4973 }
4975 NS_IMETHODIMP
4976 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth)
4977 {
4978 ErrorResult rv;
4979 SetOuterWidth(aOuterWidth, rv);
4981 return rv.ErrorCode();
4982 }
4984 void
4985 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight, ErrorResult& aError)
4986 {
4987 FORWARD_TO_OUTER_OR_THROW(SetOuterHeight, (aOuterHeight, aError), aError, );
4989 SetOuterSize(aOuterHeight, false, aError);
4990 }
4992 NS_IMETHODIMP
4993 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight)
4994 {
4995 ErrorResult rv;
4996 SetOuterHeight(aOuterHeight, rv);
4998 return rv.ErrorCode();
4999 }
5001 nsIntPoint
5002 nsGlobalWindow::GetScreenXY(ErrorResult& aError)
5003 {
5004 MOZ_ASSERT(IsOuterWindow());
5006 // For non-chrome callers, always return (0,0) to prevent fingerprinting.
5007 if (!IsChrome()) {
5008 return nsIntPoint(0, 0);
5009 }
5011 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5012 if (!treeOwnerAsWin) {
5013 aError.Throw(NS_ERROR_FAILURE);
5014 return nsIntPoint(0, 0);
5015 }
5017 int32_t x = 0, y = 0;
5018 aError = treeOwnerAsWin->GetPosition(&x, &y);
5019 return nsIntPoint(x, y);
5020 }
5022 int32_t
5023 nsGlobalWindow::GetScreenX(ErrorResult& aError)
5024 {
5025 FORWARD_TO_OUTER_OR_THROW(GetScreenX, (aError), aError, 0);
5027 return DevToCSSIntPixels(GetScreenXY(aError).x);
5028 }
5030 NS_IMETHODIMP
5031 nsGlobalWindow::GetScreenX(int32_t* aScreenX)
5032 {
5033 ErrorResult rv;
5034 *aScreenX = GetScreenX(rv);
5036 return rv.ErrorCode();
5037 }
5039 nsRect
5040 nsGlobalWindow::GetInnerScreenRect()
5041 {
5042 MOZ_ASSERT(IsOuterWindow());
5044 if (!mDocShell) {
5045 return nsRect();
5046 }
5048 nsGlobalWindow* rootWindow =
5049 static_cast<nsGlobalWindow*>(GetPrivateRoot());
5050 if (rootWindow) {
5051 rootWindow->FlushPendingNotifications(Flush_Layout);
5052 }
5054 if (!mDocShell) {
5055 return nsRect();
5056 }
5058 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
5059 if (!presShell) {
5060 return nsRect();
5061 }
5062 nsIFrame* rootFrame = presShell->GetRootFrame();
5063 if (!rootFrame) {
5064 return nsRect();
5065 }
5067 return rootFrame->GetScreenRectInAppUnits();
5068 }
5070 float
5071 nsGlobalWindow::GetMozInnerScreenX(ErrorResult& aError)
5072 {
5073 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenX, (aError), aError, 0);
5075 // For non-chrome callers, always return 0 to prevent fingerprinting.
5076 if (!IsChrome()) return 0.0;
5078 nsRect r = GetInnerScreenRect();
5079 return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
5080 }
5082 NS_IMETHODIMP
5083 nsGlobalWindow::GetMozInnerScreenX(float* aScreenX)
5084 {
5085 ErrorResult rv;
5086 *aScreenX = GetMozInnerScreenX(rv);
5088 return rv.ErrorCode();
5089 }
5091 float
5092 nsGlobalWindow::GetMozInnerScreenY(ErrorResult& aError)
5093 {
5094 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenY, (aError), aError, 0);
5096 // For non-chrome callers, always return 0 to prevent fingerprinting.
5097 if (!IsChrome()) return 0.0;
5099 nsRect r = GetInnerScreenRect();
5100 return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
5101 }
5103 NS_IMETHODIMP
5104 nsGlobalWindow::GetMozInnerScreenY(float* aScreenY)
5105 {
5106 ErrorResult rv;
5107 *aScreenY = GetMozInnerScreenY(rv);
5109 return rv.ErrorCode();
5110 }
5112 float
5113 nsGlobalWindow::GetDevicePixelRatio(ErrorResult& aError)
5114 {
5115 FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatio, (aError), aError, 0.0);
5117 if (!mDocShell) {
5118 return 1.0;
5119 }
5121 nsRefPtr<nsPresContext> presContext;
5122 mDocShell->GetPresContext(getter_AddRefs(presContext));
5123 if (!presContext) {
5124 return 1.0;
5125 }
5127 return float(nsPresContext::AppUnitsPerCSSPixel())/
5128 presContext->AppUnitsPerDevPixel();
5129 }
5131 NS_IMETHODIMP
5132 nsGlobalWindow::GetDevicePixelRatio(float* aRatio)
5133 {
5134 ErrorResult rv;
5135 *aRatio = GetDevicePixelRatio(rv);
5137 return rv.ErrorCode();
5138 }
5140 uint64_t
5141 nsGlobalWindow::GetMozPaintCount(ErrorResult& aError)
5142 {
5143 FORWARD_TO_OUTER_OR_THROW(GetMozPaintCount, (aError), aError, 0);
5145 if (!mDocShell) {
5146 return 0;
5147 }
5149 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
5150 return presShell ? presShell->GetPaintCount() : 0;
5151 }
5153 NS_IMETHODIMP
5154 nsGlobalWindow::GetMozPaintCount(uint64_t* aResult)
5155 {
5156 ErrorResult rv;
5157 *aResult = GetMozPaintCount(rv);
5159 return rv.ErrorCode();
5160 }
5162 NS_IMETHODIMP
5163 nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback,
5164 int32_t *aHandle)
5165 {
5166 if (!aCallback) {
5167 if (mDoc) {
5168 mDoc->WarnOnceAbout(nsIDocument::eMozBeforePaint);
5169 }
5170 return NS_ERROR_XPC_BAD_CONVERT_JS;
5171 }
5173 ErrorResult rv;
5174 nsIDocument::FrameRequestCallbackHolder holder(aCallback);
5175 *aHandle = RequestAnimationFrame(holder, rv);
5177 return rv.ErrorCode();
5178 }
5180 int32_t
5181 nsGlobalWindow::RequestAnimationFrame(FrameRequestCallback& aCallback,
5182 ErrorResult& aError)
5183 {
5184 nsIDocument::FrameRequestCallbackHolder holder(&aCallback);
5185 return RequestAnimationFrame(holder, aError);
5186 }
5188 int32_t
5189 nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback,
5190 ErrorResult& aError)
5191 {
5192 nsIDocument::FrameRequestCallbackHolder holder(aCallback);
5193 return RequestAnimationFrame(holder, aError);
5194 }
5196 int32_t
5197 nsGlobalWindow::RequestAnimationFrame(const nsIDocument::FrameRequestCallbackHolder& aCallback,
5198 ErrorResult& aError)
5199 {
5200 FORWARD_TO_INNER_OR_THROW(RequestAnimationFrame, (aCallback, aError), aError,
5201 0);
5203 if (!mDoc) {
5204 return 0;
5205 }
5207 if (GetWrapperPreserveColor()) {
5208 js::NotifyAnimationActivity(GetWrapperPreserveColor());
5209 }
5211 int32_t handle;
5212 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
5213 return handle;
5214 }
5216 NS_IMETHODIMP
5217 nsGlobalWindow::RequestAnimationFrame(JS::Handle<JS::Value> aCallback,
5218 JSContext* cx,
5219 int32_t* aHandle)
5220 {
5221 if (!aCallback.isObject() || !JS_ObjectIsCallable(cx, &aCallback.toObject())) {
5222 return NS_ERROR_INVALID_ARG;
5223 }
5225 JS::Rooted<JSObject*> callbackObj(cx, &aCallback.toObject());
5226 nsRefPtr<FrameRequestCallback> callback =
5227 new FrameRequestCallback(callbackObj, GetIncumbentGlobal());
5229 ErrorResult rv;
5230 *aHandle = RequestAnimationFrame(*callback, rv);
5232 return rv.ErrorCode();
5233 }
5235 NS_IMETHODIMP
5236 nsGlobalWindow::MozCancelRequestAnimationFrame(int32_t aHandle)
5237 {
5238 return CancelAnimationFrame(aHandle);
5239 }
5241 NS_IMETHODIMP
5242 nsGlobalWindow::MozCancelAnimationFrame(int32_t aHandle)
5243 {
5244 return CancelAnimationFrame(aHandle);
5245 }
5247 void
5248 nsGlobalWindow::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError)
5249 {
5250 FORWARD_TO_INNER_OR_THROW(CancelAnimationFrame, (aHandle, aError), aError, );
5252 if (!mDoc) {
5253 return;
5254 }
5256 mDoc->CancelFrameRequestCallback(aHandle);
5257 }
5259 NS_IMETHODIMP
5260 nsGlobalWindow::CancelAnimationFrame(int32_t aHandle)
5261 {
5262 ErrorResult rv;
5263 CancelAnimationFrame(aHandle, rv);
5265 return rv.ErrorCode();
5266 }
5268 int64_t
5269 nsGlobalWindow::GetMozAnimationStartTime(ErrorResult& aError)
5270 {
5271 FORWARD_TO_INNER_OR_THROW(GetMozAnimationStartTime, (aError), aError, 0);
5273 if (mDoc) {
5274 nsIPresShell* presShell = mDoc->GetShell();
5275 if (presShell) {
5276 return presShell->GetPresContext()->RefreshDriver()->
5277 MostRecentRefreshEpochTime() / PR_USEC_PER_MSEC;
5278 }
5279 }
5281 // If all else fails, just be compatible with Date.now()
5282 return JS_Now() / PR_USEC_PER_MSEC;
5283 }
5285 NS_IMETHODIMP
5286 nsGlobalWindow::GetMozAnimationStartTime(int64_t *aTime)
5287 {
5288 ErrorResult rv;
5289 *aTime = GetMozAnimationStartTime(rv);
5291 return rv.ErrorCode();
5292 }
5294 already_AddRefed<MediaQueryList>
5295 nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
5296 ErrorResult& aError)
5297 {
5298 // FIXME: This whole forward-to-outer and then get a pres
5299 // shell/context off the docshell dance is sort of silly; it'd make
5300 // more sense to forward to the inner, but it's what everyone else
5301 // (GetSelection, GetScrollXY, etc.) does around here.
5302 FORWARD_TO_OUTER_OR_THROW(MatchMedia, (aMediaQueryList, aError), aError,
5303 nullptr);
5305 // We need this now to ensure that we have a non-null |presContext|
5306 // when we ought to.
5307 // This is similar to EnsureSizeUpToDate, but only flushes frames.
5308 nsGlobalWindow *parent = static_cast<nsGlobalWindow*>(GetPrivateParent());
5309 if (parent) {
5310 parent->FlushPendingNotifications(Flush_Frames);
5311 }
5313 if (!mDocShell) {
5314 return nullptr;
5315 }
5317 nsRefPtr<nsPresContext> presContext;
5318 mDocShell->GetPresContext(getter_AddRefs(presContext));
5320 if (!presContext) {
5321 return nullptr;
5322 }
5324 return presContext->MatchMedia(aMediaQueryList);
5325 }
5327 NS_IMETHODIMP
5328 nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
5329 nsISupports** aResult)
5330 {
5331 ErrorResult rv;
5332 nsRefPtr<MediaQueryList> mediaQueryList = MatchMedia(aMediaQueryList, rv);
5333 mediaQueryList.forget(aResult);
5335 return rv.ErrorCode();
5336 }
5338 void
5339 nsGlobalWindow::SetScreenX(int32_t aScreenX, ErrorResult& aError)
5340 {
5341 FORWARD_TO_OUTER_OR_THROW(SetScreenX, (aScreenX, aError), aError, );
5343 /*
5344 * If caller is not chrome and the user has not explicitly exempted the site,
5345 * prevent setting window.screenX by exiting early
5346 */
5348 if (!CanMoveResizeWindows() || IsFrame()) {
5349 return;
5350 }
5352 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5353 if (!treeOwnerAsWin) {
5354 aError.Throw(NS_ERROR_FAILURE);
5355 return;
5356 }
5358 int32_t x, y;
5359 aError = treeOwnerAsWin->GetPosition(&x, &y);
5360 if (aError.Failed()) {
5361 return;
5362 }
5364 CheckSecurityLeftAndTop(&aScreenX, nullptr);
5365 x = CSSToDevIntPixels(aScreenX);
5367 aError = treeOwnerAsWin->SetPosition(x, y);
5368 }
5370 NS_IMETHODIMP
5371 nsGlobalWindow::SetScreenX(int32_t aScreenX)
5372 {
5373 ErrorResult rv;
5374 SetScreenX(aScreenX, rv);
5376 return rv.ErrorCode();
5377 }
5379 int32_t
5380 nsGlobalWindow::GetScreenY(ErrorResult& aError)
5381 {
5382 FORWARD_TO_OUTER_OR_THROW(GetScreenY, (aError), aError, 0);
5384 return DevToCSSIntPixels(GetScreenXY(aError).y);
5385 }
5387 NS_IMETHODIMP
5388 nsGlobalWindow::GetScreenY(int32_t* aScreenY)
5389 {
5390 ErrorResult rv;
5391 *aScreenY = GetScreenY(rv);
5393 return rv.ErrorCode();
5394 }
5396 void
5397 nsGlobalWindow::SetScreenY(int32_t aScreenY, ErrorResult& aError)
5398 {
5399 FORWARD_TO_OUTER_OR_THROW(SetScreenY, (aScreenY, aError), aError, );
5401 /*
5402 * If caller is not chrome and the user has not explicitly exempted the site,
5403 * prevent setting window.screenY by exiting early
5404 */
5406 if (!CanMoveResizeWindows() || IsFrame()) {
5407 return;
5408 }
5410 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5411 if (!treeOwnerAsWin) {
5412 aError.Throw(NS_ERROR_FAILURE);
5413 return;
5414 }
5416 int32_t x, y;
5417 aError = treeOwnerAsWin->GetPosition(&x, &y);
5418 if (aError.Failed()) {
5419 return;
5420 }
5422 CheckSecurityLeftAndTop(nullptr, &aScreenY);
5423 y = CSSToDevIntPixels(aScreenY);
5425 aError = treeOwnerAsWin->SetPosition(x, y);
5426 }
5428 NS_IMETHODIMP
5429 nsGlobalWindow::SetScreenY(int32_t aScreenY)
5430 {
5431 ErrorResult rv;
5432 SetScreenY(aScreenY, rv);
5434 return rv.ErrorCode();
5435 }
5437 bool
5438 nsGlobalWindow::IsChrome() const
5439 {
5440 bool isChrome = false;
5442 if (mDocShell) {
5443 nsRefPtr<nsPresContext> presContext;
5444 mDocShell->GetPresContext(getter_AddRefs(presContext));
5445 isChrome = (presContext && presContext->IsChrome());
5446 }
5448 return isChrome;
5449 }
5451 // NOTE: Arguments to this function should have values scaled to
5452 // CSS pixels, not device pixels.
5453 void
5454 nsGlobalWindow::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight)
5455 {
5456 MOZ_ASSERT(IsOuterWindow());
5458 #ifdef MOZ_XUL
5459 if (!nsContentUtils::IsCallerChrome()) {
5460 // if attempting to resize the window, hide any open popups
5461 nsContentUtils::HidePopupsInDocument(mDoc);
5462 }
5463 #endif
5465 // This one is easy. Just ensure the variable is greater than 100;
5466 if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
5467 // Check security state for use in determing window dimensions
5469 if (!nsContentUtils::IsCallerChrome()) {
5470 //sec check failed
5471 if (aWidth && *aWidth < 100) {
5472 *aWidth = 100;
5473 }
5474 if (aHeight && *aHeight < 100) {
5475 *aHeight = 100;
5476 }
5477 }
5478 }
5479 }
5481 // NOTE: Arguments to this function should have values in device pixels
5482 nsresult
5483 nsGlobalWindow::SetDocShellWidthAndHeight(int32_t aInnerWidth, int32_t aInnerHeight)
5484 {
5485 MOZ_ASSERT(IsOuterWindow());
5487 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
5489 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
5490 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
5491 NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
5493 NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(mDocShell, aInnerWidth, aInnerHeight),
5494 NS_ERROR_FAILURE);
5496 return NS_OK;
5497 }
5499 // NOTE: Arguments to this function should have values in app units
5500 void
5501 nsGlobalWindow::SetCSSViewportWidthAndHeight(nscoord aInnerWidth, nscoord aInnerHeight)
5502 {
5503 MOZ_ASSERT(IsOuterWindow());
5505 nsRefPtr<nsPresContext> presContext;
5506 mDocShell->GetPresContext(getter_AddRefs(presContext));
5508 nsRect shellArea = presContext->GetVisibleArea();
5509 shellArea.height = aInnerHeight;
5510 shellArea.width = aInnerWidth;
5512 presContext->SetVisibleArea(shellArea);
5513 }
5515 // NOTE: Arguments to this function should have values scaled to
5516 // CSS pixels, not device pixels.
5517 void
5518 nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop)
5519 {
5520 MOZ_ASSERT(IsOuterWindow());
5522 // This one is harder. We have to get the screen size and window dimensions.
5524 // Check security state for use in determing window dimensions
5526 if (!nsContentUtils::IsCallerChrome()) {
5527 #ifdef MOZ_XUL
5528 // if attempting to move the window, hide any open popups
5529 nsContentUtils::HidePopupsInDocument(mDoc);
5530 #endif
5532 nsGlobalWindow* rootWindow =
5533 static_cast<nsGlobalWindow*>(GetPrivateRoot());
5534 if (rootWindow) {
5535 rootWindow->FlushPendingNotifications(Flush_Layout);
5536 }
5538 nsCOMPtr<nsIBaseWindow> treeOwner = GetTreeOwnerWindow();
5540 nsCOMPtr<nsIDOMScreen> screen;
5541 GetScreen(getter_AddRefs(screen));
5543 if (treeOwner && screen) {
5544 int32_t screenLeft, screenTop, screenWidth, screenHeight;
5545 int32_t winLeft, winTop, winWidth, winHeight;
5547 // Get the window size
5548 treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
5550 // convert those values to CSS pixels
5551 // XXX four separate retrievals of the prescontext
5552 winLeft = DevToCSSIntPixels(winLeft);
5553 winTop = DevToCSSIntPixels(winTop);
5554 winWidth = DevToCSSIntPixels(winWidth);
5555 winHeight = DevToCSSIntPixels(winHeight);
5557 // Get the screen dimensions
5558 // XXX This should use nsIScreenManager once it's fully fleshed out.
5559 screen->GetAvailLeft(&screenLeft);
5560 screen->GetAvailWidth(&screenWidth);
5561 screen->GetAvailHeight(&screenHeight);
5562 #if defined(XP_MACOSX)
5563 /* The mac's coordinate system is different from the assumed Windows'
5564 system. It offsets by the height of the menubar so that a window
5565 placed at (0,0) will be entirely visible. Unfortunately that
5566 correction is made elsewhere (in Widget) and the meaning of
5567 the Avail... coordinates is overloaded. Here we allow a window
5568 to be placed at (0,0) because it does make sense to do so.
5569 */
5570 screen->GetTop(&screenTop);
5571 #else
5572 screen->GetAvailTop(&screenTop);
5573 #endif
5575 if (aLeft) {
5576 if (screenLeft+screenWidth < *aLeft+winWidth)
5577 *aLeft = screenLeft+screenWidth - winWidth;
5578 if (screenLeft > *aLeft)
5579 *aLeft = screenLeft;
5580 }
5581 if (aTop) {
5582 if (screenTop+screenHeight < *aTop+winHeight)
5583 *aTop = screenTop+screenHeight - winHeight;
5584 if (screenTop > *aTop)
5585 *aTop = screenTop;
5586 }
5587 } else {
5588 if (aLeft)
5589 *aLeft = 0;
5590 if (aTop)
5591 *aTop = 0;
5592 }
5593 }
5594 }
5596 NS_IMETHODIMP
5597 nsGlobalWindow::GetPageXOffset(int32_t* aPageXOffset)
5598 {
5599 return GetScrollX(aPageXOffset);
5600 }
5602 NS_IMETHODIMP
5603 nsGlobalWindow::GetPageYOffset(int32_t* aPageYOffset)
5604 {
5605 return GetScrollY(aPageYOffset);
5606 }
5608 void
5609 nsGlobalWindow::GetScrollMaxXY(int32_t* aScrollMaxX, int32_t* aScrollMaxY,
5610 ErrorResult& aError)
5611 {
5612 FORWARD_TO_OUTER_OR_THROW(GetScrollMaxXY, (aScrollMaxX, aScrollMaxY, aError),
5613 aError, );
5615 FlushPendingNotifications(Flush_Layout);
5616 nsIScrollableFrame *sf = GetScrollFrame();
5617 if (!sf) {
5618 return;
5619 }
5621 nsRect scrollRange = sf->GetScrollRange();
5623 if (aScrollMaxX) {
5624 *aScrollMaxX = std::max(0,
5625 (int32_t)floor(nsPresContext::AppUnitsToFloatCSSPixels(scrollRange.XMost())));
5626 }
5627 if (aScrollMaxY) {
5628 *aScrollMaxY = std::max(0,
5629 (int32_t)floor(nsPresContext::AppUnitsToFloatCSSPixels(scrollRange.YMost())));
5630 }
5631 }
5633 int32_t
5634 nsGlobalWindow::GetScrollMaxX(ErrorResult& aError)
5635 {
5636 int32_t scrollMaxX = 0;
5637 GetScrollMaxXY(&scrollMaxX, nullptr, aError);
5638 return scrollMaxX;
5639 }
5641 NS_IMETHODIMP
5642 nsGlobalWindow::GetScrollMaxX(int32_t* aScrollMaxX)
5643 {
5644 NS_ENSURE_ARG_POINTER(aScrollMaxX);
5645 ErrorResult rv;
5646 *aScrollMaxX = GetScrollMaxX(rv);
5648 return rv.ErrorCode();
5649 }
5651 int32_t
5652 nsGlobalWindow::GetScrollMaxY(ErrorResult& aError)
5653 {
5654 int32_t scrollMaxY = 0;
5655 GetScrollMaxXY(nullptr, &scrollMaxY, aError);
5656 return scrollMaxY;
5657 }
5659 NS_IMETHODIMP
5660 nsGlobalWindow::GetScrollMaxY(int32_t* aScrollMaxY)
5661 {
5662 NS_ENSURE_ARG_POINTER(aScrollMaxY);
5663 ErrorResult rv;
5664 *aScrollMaxY = GetScrollMaxY(rv);
5666 return rv.ErrorCode();
5667 }
5669 CSSIntPoint
5670 nsGlobalWindow::GetScrollXY(bool aDoFlush, ErrorResult& aError)
5671 {
5672 FORWARD_TO_OUTER_OR_THROW(GetScrollXY, (aDoFlush, aError), aError,
5673 CSSIntPoint(0, 0));
5675 if (aDoFlush) {
5676 FlushPendingNotifications(Flush_Layout);
5677 } else {
5678 EnsureSizeUpToDate();
5679 }
5681 nsIScrollableFrame *sf = GetScrollFrame();
5682 if (!sf) {
5683 return CSSIntPoint(0, 0);
5684 }
5686 nsPoint scrollPos = sf->GetScrollPosition();
5687 if (scrollPos != nsPoint(0,0) && !aDoFlush) {
5688 // Oh, well. This is the expensive case -- the window is scrolled and we
5689 // didn't actually flush yet. Repeat, but with a flush, since the content
5690 // may get shorter and hence our scroll position may decrease.
5691 return GetScrollXY(true, aError);
5692 }
5694 return sf->GetScrollPositionCSSPixels();
5695 }
5697 int32_t
5698 nsGlobalWindow::GetScrollX(ErrorResult& aError)
5699 {
5700 return GetScrollXY(false, aError).x;
5701 }
5703 NS_IMETHODIMP
5704 nsGlobalWindow::GetScrollX(int32_t* aScrollX)
5705 {
5706 NS_ENSURE_ARG_POINTER(aScrollX);
5707 ErrorResult rv;
5708 *aScrollX = GetScrollXY(false, rv).x;
5709 return rv.ErrorCode();
5710 }
5712 int32_t
5713 nsGlobalWindow::GetScrollY(ErrorResult& aError)
5714 {
5715 return GetScrollXY(false, aError).y;
5716 }
5718 NS_IMETHODIMP
5719 nsGlobalWindow::GetScrollY(int32_t* aScrollY)
5720 {
5721 NS_ENSURE_ARG_POINTER(aScrollY);
5722 ErrorResult rv;
5723 *aScrollY = GetScrollXY(false, rv).y;
5724 return rv.ErrorCode();
5725 }
5727 uint32_t
5728 nsGlobalWindow::Length()
5729 {
5730 FORWARD_TO_OUTER(Length, (), 0);
5732 nsDOMWindowList* windows = GetWindowList();
5734 return windows ? windows->GetLength() : 0;
5735 }
5737 NS_IMETHODIMP
5738 nsGlobalWindow::GetLength(uint32_t* aLength)
5739 {
5740 *aLength = Length();
5741 return NS_OK;
5742 }
5744 already_AddRefed<nsIDOMWindow>
5745 nsGlobalWindow::GetChildWindow(const nsAString& aName)
5746 {
5747 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
5748 NS_ENSURE_TRUE(docShell, nullptr);
5750 nsCOMPtr<nsIDocShellTreeItem> child;
5751 docShell->FindChildWithName(PromiseFlatString(aName).get(),
5752 false, true, nullptr, nullptr,
5753 getter_AddRefs(child));
5755 nsCOMPtr<nsIDOMWindow> child_win(do_GetInterface(child));
5756 return child_win.forget();
5757 }
5759 bool
5760 nsGlobalWindow::DispatchCustomEvent(const char *aEventName)
5761 {
5762 bool defaultActionEnabled = true;
5763 nsContentUtils::DispatchTrustedEvent(mDoc,
5764 GetOuterWindow(),
5765 NS_ConvertASCIItoUTF16(aEventName),
5766 true, true, &defaultActionEnabled);
5768 return defaultActionEnabled;
5769 }
5771 // NOTE: Arguments to this function should be CSS pixels, not device pixels.
5772 bool
5773 nsGlobalWindow::DispatchResizeEvent(const nsIntSize& aSize)
5774 {
5775 ErrorResult res;
5776 nsRefPtr<Event> domEvent =
5777 mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res);
5778 if (res.Failed()) {
5779 return false;
5780 }
5782 AutoSafeJSContext cx;
5783 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
5784 DOMWindowResizeEventDetail detail;
5785 detail.mWidth = aSize.width;
5786 detail.mHeight = aSize.height;
5787 JS::Rooted<JS::Value> detailValue(cx);
5788 if (!detail.ToObject(cx, &detailValue)) {
5789 return false;
5790 }
5792 CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
5793 customEvent->InitCustomEvent(cx,
5794 NS_LITERAL_STRING("DOMWindowResize"),
5795 /* bubbles = */ true,
5796 /* cancelable = */ true,
5797 detailValue,
5798 res);
5799 if (res.Failed()) {
5800 return false;
5801 }
5803 domEvent->SetTrusted(true);
5804 domEvent->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
5806 nsCOMPtr<EventTarget> target = do_QueryInterface(GetOuterWindow());
5807 domEvent->SetTarget(target);
5809 bool defaultActionEnabled = true;
5810 target->DispatchEvent(domEvent, &defaultActionEnabled);
5812 return defaultActionEnabled;
5813 }
5815 void
5816 nsGlobalWindow::RefreshCompartmentPrincipal()
5817 {
5818 FORWARD_TO_INNER(RefreshCompartmentPrincipal, (), /* void */ );
5820 JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
5821 nsJSPrincipals::get(mDoc->NodePrincipal()));
5822 }
5824 static already_AddRefed<nsIDocShellTreeItem>
5825 GetCallerDocShellTreeItem()
5826 {
5827 JSContext *cx = nsContentUtils::GetCurrentJSContext();
5828 nsCOMPtr<nsIDocShellTreeItem> callerItem;
5830 if (cx) {
5831 nsCOMPtr<nsIWebNavigation> callerWebNav =
5832 do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx));
5834 callerItem = do_QueryInterface(callerWebNav);
5835 }
5837 return callerItem.forget();
5838 }
5840 bool
5841 nsGlobalWindow::WindowExists(const nsAString& aName,
5842 bool aLookForCallerOnJSStack)
5843 {
5844 NS_PRECONDITION(IsOuterWindow(), "Must be outer window");
5845 NS_PRECONDITION(mDocShell, "Must have docshell");
5847 nsCOMPtr<nsIDocShellTreeItem> caller;
5848 if (aLookForCallerOnJSStack) {
5849 caller = GetCallerDocShellTreeItem();
5850 }
5852 if (!caller) {
5853 caller = mDocShell;
5854 }
5856 nsCOMPtr<nsIDocShellTreeItem> namedItem;
5857 mDocShell->FindItemWithName(PromiseFlatString(aName).get(), nullptr, caller,
5858 getter_AddRefs(namedItem));
5859 return namedItem != nullptr;
5860 }
5862 already_AddRefed<nsIWidget>
5863 nsGlobalWindow::GetMainWidget()
5864 {
5865 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
5867 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5869 nsCOMPtr<nsIWidget> widget;
5871 if (treeOwnerAsWin) {
5872 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
5873 }
5875 return widget.forget();
5876 }
5878 nsIWidget*
5879 nsGlobalWindow::GetNearestWidget()
5880 {
5881 nsIDocShell* docShell = GetDocShell();
5882 NS_ENSURE_TRUE(docShell, nullptr);
5883 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
5884 NS_ENSURE_TRUE(presShell, nullptr);
5885 nsIFrame* rootFrame = presShell->GetRootFrame();
5886 NS_ENSURE_TRUE(rootFrame, nullptr);
5887 return rootFrame->GetView()->GetNearestWidget(nullptr);
5888 }
5890 void
5891 nsGlobalWindow::SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError)
5892 {
5893 aError = SetFullScreenInternal(aFullScreen, true);
5894 }
5896 NS_IMETHODIMP
5897 nsGlobalWindow::SetFullScreen(bool aFullScreen)
5898 {
5899 return SetFullScreenInternal(aFullScreen, true);
5900 }
5902 nsresult
5903 nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
5904 {
5905 FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);
5907 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
5909 bool rootWinFullScreen;
5910 GetFullScreen(&rootWinFullScreen);
5911 // Only chrome can change our fullScreen mode, unless we're running in
5912 // untrusted mode.
5913 if (aFullScreen == rootWinFullScreen ||
5914 (aRequireTrust && !nsContentUtils::IsCallerChrome())) {
5915 return NS_OK;
5916 }
5918 // SetFullScreen needs to be called on the root window, so get that
5919 // via the DocShell tree, and if we are not already the root,
5920 // call SetFullScreen on that window instead.
5921 nsCOMPtr<nsIDocShellTreeItem> rootItem;
5922 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
5923 nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(rootItem);
5924 if (!window)
5925 return NS_ERROR_FAILURE;
5926 if (rootItem != mDocShell)
5927 return window->SetFullScreenInternal(aFullScreen, aRequireTrust);
5929 // make sure we don't try to set full screen on a non-chrome window,
5930 // which might happen in embedding world
5931 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome)
5932 return NS_ERROR_FAILURE;
5934 // If we are already in full screen mode, just return.
5935 if (mFullScreen == aFullScreen)
5936 return NS_OK;
5938 // dispatch a "fullscreen" DOM event so that XUL apps can
5939 // respond visually if we are kicked into full screen mode
5940 if (!DispatchCustomEvent("fullscreen")) {
5941 return NS_OK;
5942 }
5944 // Prevent chrome documents which are still loading from resizing
5945 // the window after we set fullscreen mode.
5946 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5947 nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwnerAsWin));
5948 if (aFullScreen && xulWin) {
5949 xulWin->SetIntrinsicallySized(false);
5950 }
5952 // Set this before so if widget sends an event indicating its
5953 // gone full screen, the state trap above works.
5954 mFullScreen = aFullScreen;
5956 // Sometimes we don't want the top-level widget to actually go fullscreen,
5957 // for example in the B2G desktop client, we don't want the emulated screen
5958 // dimensions to appear to increase when entering fullscreen mode; we just
5959 // want the content to fill the entire client area of the emulator window.
5960 if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) {
5961 nsCOMPtr<nsIWidget> widget = GetMainWidget();
5962 if (widget)
5963 widget->MakeFullScreen(aFullScreen);
5964 }
5966 if (!mFullScreen) {
5967 // Force exit from DOM full-screen mode. This is so that if we're in
5968 // DOM full-screen mode and the user exits full-screen mode with
5969 // the browser full-screen mode toggle keyboard-shortcut, we'll detect
5970 // that and leave DOM API full-screen mode too.
5971 nsIDocument::ExitFullscreen(mDoc, /* async */ false);
5972 }
5974 if (!mWakeLock && mFullScreen) {
5975 nsRefPtr<power::PowerManagerService> pmService =
5976 power::PowerManagerService::GetInstance();
5977 NS_ENSURE_TRUE(pmService, NS_OK);
5979 ErrorResult rv;
5980 mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"),
5981 this, rv);
5982 if (rv.Failed()) {
5983 return rv.ErrorCode();
5984 }
5986 } else if (mWakeLock && !mFullScreen) {
5987 ErrorResult rv;
5988 mWakeLock->Unlock(rv);
5989 NS_WARN_IF_FALSE(!rv.Failed(), "Failed to unlock the wakelock.");
5990 mWakeLock = nullptr;
5991 }
5993 return NS_OK;
5994 }
5996 bool
5997 nsGlobalWindow::GetFullScreen(ErrorResult& aError)
5998 {
5999 FORWARD_TO_OUTER_OR_THROW(GetFullScreen, (aError), aError, false);
6001 // Get the fullscreen value of the root window, to always have the value
6002 // accurate, even when called from content.
6003 if (mDocShell) {
6004 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6005 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6006 if (rootItem != mDocShell) {
6007 nsCOMPtr<nsIDOMWindow> window = do_GetInterface(rootItem);
6008 if (window) {
6009 bool fullScreen = false;
6010 aError = window->GetFullScreen(&fullScreen);
6011 return fullScreen;
6012 }
6013 }
6014 }
6016 // We are the root window, or something went wrong. Return our internal value.
6017 return mFullScreen;
6018 }
6020 NS_IMETHODIMP
6021 nsGlobalWindow::GetFullScreen(bool* aFullScreen)
6022 {
6023 ErrorResult rv;
6024 *aFullScreen = GetFullScreen(rv);
6026 return rv.ErrorCode();
6027 }
6029 NS_IMETHODIMP
6030 nsGlobalWindow::Dump(const nsAString& aStr)
6031 {
6032 if (!nsContentUtils::DOMWindowDumpEnabled()) {
6033 return NS_OK;
6034 }
6036 char *cstr = ToNewUTF8String(aStr);
6038 #if defined(XP_MACOSX)
6039 // have to convert \r to \n so that printing to the console works
6040 char *c = cstr, *cEnd = cstr + strlen(cstr);
6041 while (c < cEnd) {
6042 if (*c == '\r')
6043 *c = '\n';
6044 c++;
6045 }
6046 #endif
6048 if (cstr) {
6049 #ifdef XP_WIN
6050 PrintToDebugger(cstr);
6051 #endif
6052 #ifdef ANDROID
6053 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
6054 #endif
6055 FILE *fp = gDumpFile ? gDumpFile : stdout;
6056 fputs(cstr, fp);
6057 fflush(fp);
6058 nsMemory::Free(cstr);
6059 }
6061 return NS_OK;
6062 }
6064 void
6065 nsGlobalWindow::EnsureReflowFlushAndPaint()
6066 {
6067 MOZ_ASSERT(IsOuterWindow());
6068 NS_ASSERTION(mDocShell, "EnsureReflowFlushAndPaint() called with no "
6069 "docshell!");
6071 if (!mDocShell)
6072 return;
6074 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
6076 if (!presShell)
6077 return;
6079 // Flush pending reflows.
6080 if (mDoc) {
6081 mDoc->FlushPendingNotifications(Flush_Layout);
6082 }
6084 // Unsuppress painting.
6085 presShell->UnsuppressPainting();
6086 }
6088 NS_IMETHODIMP
6089 nsGlobalWindow::GetTextZoom(float *aZoom)
6090 {
6091 FORWARD_TO_OUTER(GetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED);
6093 if (mDocShell) {
6094 nsCOMPtr<nsIContentViewer> contentViewer;
6095 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
6096 nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(contentViewer));
6098 if (markupViewer) {
6099 return markupViewer->GetTextZoom(aZoom);
6100 }
6101 }
6102 return NS_ERROR_FAILURE;
6103 }
6105 NS_IMETHODIMP
6106 nsGlobalWindow::SetTextZoom(float aZoom)
6107 {
6108 FORWARD_TO_OUTER(SetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED);
6110 if (mDocShell) {
6111 nsCOMPtr<nsIContentViewer> contentViewer;
6112 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
6113 nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(contentViewer));
6115 if (markupViewer)
6116 return markupViewer->SetTextZoom(aZoom);
6117 }
6118 return NS_ERROR_FAILURE;
6119 }
6121 // static
6122 void
6123 nsGlobalWindow::MakeScriptDialogTitle(nsAString &aOutTitle)
6124 {
6125 aOutTitle.Truncate();
6127 // Try to get a host from the running principal -- this will do the
6128 // right thing for javascript: and data: documents.
6130 nsresult rv = NS_OK;
6131 NS_ASSERTION(nsContentUtils::GetSecurityManager(),
6132 "Global Window has no security manager!");
6133 if (nsContentUtils::GetSecurityManager()) {
6134 nsCOMPtr<nsIPrincipal> principal;
6135 rv = nsContentUtils::GetSecurityManager()->
6136 GetSubjectPrincipal(getter_AddRefs(principal));
6138 if (NS_SUCCEEDED(rv) && principal) {
6139 nsCOMPtr<nsIURI> uri;
6140 rv = principal->GetURI(getter_AddRefs(uri));
6142 if (NS_SUCCEEDED(rv) && uri) {
6143 // remove user:pass for privacy and spoof prevention
6145 nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID));
6146 if (fixup) {
6147 nsCOMPtr<nsIURI> fixedURI;
6148 rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
6149 if (NS_SUCCEEDED(rv) && fixedURI) {
6150 nsAutoCString host;
6151 fixedURI->GetHost(host);
6153 if (!host.IsEmpty()) {
6154 // if this URI has a host we'll show it. For other
6155 // schemes (e.g. file:) we fall back to the localized
6156 // generic string
6158 nsAutoCString prepath;
6159 fixedURI->GetPrePath(prepath);
6161 NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
6162 const char16_t *formatStrings[] = { ucsPrePath.get() };
6163 nsXPIDLString tempString;
6164 nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6165 "ScriptDlgHeading",
6166 formatStrings,
6167 tempString);
6168 aOutTitle = tempString;
6169 }
6170 }
6171 }
6172 }
6173 }
6174 else { // failed to get subject principal
6175 NS_WARNING("No script principal? Who is calling alert/confirm/prompt?!");
6176 }
6177 }
6179 if (aOutTitle.IsEmpty()) {
6180 // We didn't find a host so use the generic heading
6181 nsXPIDLString tempString;
6182 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6183 "ScriptDlgGenericHeading",
6184 tempString);
6185 aOutTitle = tempString;
6186 }
6188 // Just in case
6189 if (aOutTitle.IsEmpty()) {
6190 NS_WARNING("could not get ScriptDlgGenericHeading string from string bundle");
6191 aOutTitle.AssignLiteral("[Script]");
6192 }
6193 }
6195 bool
6196 nsGlobalWindow::CanMoveResizeWindows()
6197 {
6198 MOZ_ASSERT(IsOuterWindow());
6200 // When called from chrome, we can avoid the following checks.
6201 if (!nsContentUtils::IsCallerChrome()) {
6202 // Don't allow scripts to move or resize windows that were not opened by a
6203 // script.
6204 if (!mHadOriginalOpener) {
6205 return false;
6206 }
6208 if (!CanSetProperty("dom.disable_window_move_resize")) {
6209 return false;
6210 }
6212 // Ignore the request if we have more than one tab in the window.
6213 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6214 if (treeOwner) {
6215 uint32_t itemCount;
6216 if (NS_SUCCEEDED(treeOwner->GetTargetableShellCount(&itemCount)) &&
6217 itemCount > 1) {
6218 return false;
6219 }
6220 }
6221 }
6223 // The preference is useful for the webapp runtime. Webapps should be able
6224 // to resize or move their window.
6225 if (mDocShell && !Preferences::GetBool("dom.always_allow_move_resize_window",
6226 false)) {
6227 bool allow;
6228 nsresult rv = mDocShell->GetAllowWindowControl(&allow);
6229 if (NS_SUCCEEDED(rv) && !allow)
6230 return false;
6231 }
6233 if (gMouseDown && !gDragServiceDisabled) {
6234 nsCOMPtr<nsIDragService> ds =
6235 do_GetService("@mozilla.org/widget/dragservice;1");
6236 if (ds) {
6237 gDragServiceDisabled = true;
6238 ds->Suppress();
6239 }
6240 }
6241 return true;
6242 }
6244 bool
6245 nsGlobalWindow::AlertOrConfirm(bool aAlert,
6246 const nsAString& aMessage,
6247 mozilla::ErrorResult& aError)
6248 {
6249 // XXX This method is very similar to nsGlobalWindow::Prompt, make
6250 // sure any modifications here don't need to happen over there!
6251 MOZ_ASSERT(IsOuterWindow());
6253 if (!AreDialogsEnabled()) {
6254 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6255 return false;
6256 }
6258 // Reset popup state while opening a modal dialog, and firing events
6259 // about the dialog, to prevent the current state from being active
6260 // the whole time a modal dialog is open.
6261 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
6263 // Before bringing up the window, unsuppress painting and flush
6264 // pending reflows.
6265 EnsureReflowFlushAndPaint();
6267 nsAutoString title;
6268 MakeScriptDialogTitle(title);
6270 // Remove non-terminating null characters from the
6271 // string. See bug #310037.
6272 nsAutoString final;
6273 nsContentUtils::StripNullChars(aMessage, final);
6275 nsresult rv;
6276 nsCOMPtr<nsIPromptFactory> promptFac =
6277 do_GetService("@mozilla.org/prompter;1", &rv);
6278 if (NS_FAILED(rv)) {
6279 aError.Throw(rv);
6280 return false;
6281 }
6283 nsCOMPtr<nsIPrompt> prompt;
6284 aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
6285 getter_AddRefs(prompt));
6286 if (aError.Failed()) {
6287 return false;
6288 }
6290 // Always allow tab modal prompts for alert and confirm.
6291 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
6292 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
6293 }
6295 bool result = false;
6296 nsAutoSyncOperation sync(mDoc);
6297 if (ShouldPromptToBlockDialogs()) {
6298 bool disallowDialog = false;
6299 nsXPIDLString label;
6300 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6301 "ScriptDialogLabel", label);
6303 aError = aAlert ?
6304 prompt->AlertCheck(title.get(), final.get(), label.get(),
6305 &disallowDialog) :
6306 prompt->ConfirmCheck(title.get(), final.get(), label.get(),
6307 &disallowDialog, &result);
6309 if (disallowDialog)
6310 DisableDialogs();
6311 } else {
6312 aError = aAlert ?
6313 prompt->Alert(title.get(), final.get()) :
6314 prompt->Confirm(title.get(), final.get(), &result);
6315 }
6317 return result;
6318 }
6320 void
6321 nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError)
6322 {
6323 FORWARD_TO_OUTER_OR_THROW(Alert, (aMessage, aError), aError, );
6324 AlertOrConfirm(/* aAlert = */ true, aMessage, aError);
6325 }
6327 NS_IMETHODIMP
6328 nsGlobalWindow::Alert(const nsAString& aString)
6329 {
6330 ErrorResult rv;
6331 Alert(aString, rv);
6333 return rv.ErrorCode();
6334 }
6336 bool
6337 nsGlobalWindow::Confirm(const nsAString& aMessage, ErrorResult& aError)
6338 {
6339 FORWARD_TO_OUTER_OR_THROW(Confirm, (aMessage, aError), aError, false);
6341 return AlertOrConfirm(/* aAlert = */ false, aMessage, aError);
6342 }
6344 NS_IMETHODIMP
6345 nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
6346 {
6347 ErrorResult rv;
6348 *aReturn = Confirm(aString, rv);
6350 return rv.ErrorCode();
6351 }
6353 void
6354 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
6355 nsAString& aReturn, ErrorResult& aError)
6356 {
6357 // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
6358 // sure any modifications here don't need to happen over there!
6359 FORWARD_TO_OUTER_OR_THROW(Prompt, (aMessage, aInitial, aReturn, aError),
6360 aError, );
6362 SetDOMStringToNull(aReturn);
6364 if (!AreDialogsEnabled()) {
6365 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6366 return;
6367 }
6369 // Reset popup state while opening a modal dialog, and firing events
6370 // about the dialog, to prevent the current state from being active
6371 // the whole time a modal dialog is open.
6372 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
6374 // Before bringing up the window, unsuppress painting and flush
6375 // pending reflows.
6376 EnsureReflowFlushAndPaint();
6378 nsAutoString title;
6379 MakeScriptDialogTitle(title);
6381 // Remove non-terminating null characters from the
6382 // string. See bug #310037.
6383 nsAutoString fixedMessage, fixedInitial;
6384 nsContentUtils::StripNullChars(aMessage, fixedMessage);
6385 nsContentUtils::StripNullChars(aInitial, fixedInitial);
6387 nsresult rv;
6388 nsCOMPtr<nsIPromptFactory> promptFac =
6389 do_GetService("@mozilla.org/prompter;1", &rv);
6390 if (NS_FAILED(rv)) {
6391 aError.Throw(rv);
6392 return;
6393 }
6395 nsCOMPtr<nsIPrompt> prompt;
6396 aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
6397 getter_AddRefs(prompt));
6398 if (aError.Failed()) {
6399 return;
6400 }
6402 // Always allow tab modal prompts for prompt.
6403 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) {
6404 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
6405 }
6407 // Pass in the default value, if any.
6408 char16_t *inoutValue = ToNewUnicode(fixedInitial);
6409 bool disallowDialog = false;
6411 nsXPIDLString label;
6412 if (ShouldPromptToBlockDialogs()) {
6413 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6414 "ScriptDialogLabel", label);
6415 }
6417 nsAutoSyncOperation sync(mDoc);
6418 bool ok;
6419 aError = prompt->Prompt(title.get(), fixedMessage.get(),
6420 &inoutValue, label.get(), &disallowDialog, &ok);
6422 if (disallowDialog) {
6423 DisableDialogs();
6424 }
6426 if (aError.Failed()) {
6427 return;
6428 }
6430 nsAdoptingString outValue(inoutValue);
6432 if (ok && outValue) {
6433 aReturn.Assign(outValue);
6434 }
6435 }
6437 NS_IMETHODIMP
6438 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
6439 nsAString& aReturn)
6440 {
6441 ErrorResult rv;
6442 Prompt(aMessage, aInitial, aReturn, rv);
6444 return rv.ErrorCode();
6445 }
6447 void
6448 nsGlobalWindow::Focus(ErrorResult& aError)
6449 {
6450 FORWARD_TO_OUTER_OR_THROW(Focus, (aError), aError, );
6452 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6453 if (!fm) {
6454 return;
6455 }
6457 nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
6459 bool isVisible = false;
6460 if (baseWin) {
6461 baseWin->GetVisibility(&isVisible);
6462 }
6464 if (!isVisible) {
6465 // A hidden tab is being focused, ignore this call.
6466 return;
6467 }
6469 nsIDOMWindow *caller = nsContentUtils::GetWindowFromCaller();
6470 nsCOMPtr<nsIDOMWindow> opener;
6471 GetOpener(getter_AddRefs(opener));
6473 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
6474 // window which opened us to raise us at times when popups are allowed
6475 // (bugs 355482 and 369306).
6476 bool canFocus = CanSetProperty("dom.disable_window_flip") ||
6477 (opener == caller &&
6478 RevisePopupAbuseLevel(gPopupControlState) < openAbused);
6480 nsCOMPtr<nsIDOMWindow> activeWindow;
6481 fm->GetActiveWindow(getter_AddRefs(activeWindow));
6483 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6484 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6485 nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem);
6486 bool isActive = (rootWin == activeWindow);
6488 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6489 if (treeOwnerAsWin && (canFocus || isActive)) {
6490 bool isEnabled = true;
6491 if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) {
6492 NS_WARNING( "Should not try to set the focus on a disabled window" );
6493 return;
6494 }
6496 // XXXndeakin not sure what this is for or if it should go somewhere else
6497 nsCOMPtr<nsIEmbeddingSiteWindow> embeddingWin(do_GetInterface(treeOwnerAsWin));
6498 if (embeddingWin)
6499 embeddingWin->SetFocus();
6500 }
6502 if (!mDocShell) {
6503 return;
6504 }
6506 nsCOMPtr<nsIPresShell> presShell;
6507 // Don't look for a presshell if we're a root chrome window that's got
6508 // about:blank loaded. We don't want to focus our widget in that case.
6509 // XXXbz should we really be checking for IsInitialDocument() instead?
6510 bool lookForPresShell = true;
6511 if (mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome &&
6512 GetPrivateRoot() == static_cast<nsIDOMWindow*>(this) &&
6513 mDoc) {
6514 nsIURI* ourURI = mDoc->GetDocumentURI();
6515 if (ourURI) {
6516 lookForPresShell = !NS_IsAboutBlank(ourURI);
6517 }
6518 }
6520 if (lookForPresShell) {
6521 mDocShell->GetEldestPresShell(getter_AddRefs(presShell));
6522 }
6524 nsCOMPtr<nsIDocShellTreeItem> parentDsti;
6525 mDocShell->GetParent(getter_AddRefs(parentDsti));
6527 // set the parent's current focus to the frame containing this window.
6528 nsCOMPtr<nsPIDOMWindow> parent = do_GetInterface(parentDsti);
6529 if (parent) {
6530 nsCOMPtr<nsIDocument> parentdoc = parent->GetDoc();
6531 if (!parentdoc) {
6532 return;
6533 }
6535 nsIContent* frame = parentdoc->FindContentForSubDocument(mDoc);
6536 nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(frame);
6537 if (frameElement) {
6538 uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
6539 if (canFocus)
6540 flags |= nsIFocusManager::FLAG_RAISE;
6541 aError = fm->SetFocus(frameElement, flags);
6542 }
6543 return;
6544 }
6545 if (nsCOMPtr<nsITabChild> child = do_GetInterface(mDocShell)) {
6546 child->SendRequestFocus(canFocus);
6547 return;
6548 }
6549 if (canFocus) {
6550 // if there is no parent, this must be a toplevel window, so raise the
6551 // window if canFocus is true
6552 aError = fm->SetActiveWindow(this);
6553 }
6554 }
6556 NS_IMETHODIMP
6557 nsGlobalWindow::Focus()
6558 {
6559 ErrorResult rv;
6560 Focus(rv);
6562 return rv.ErrorCode();
6563 }
6565 void
6566 nsGlobalWindow::Blur(ErrorResult& aError)
6567 {
6568 FORWARD_TO_OUTER_OR_THROW(Blur, (aError), aError, );
6570 // If dom.disable_window_flip == true, then content should not be allowed
6571 // to call this function (this would allow popunders, bug 369306)
6572 if (!CanSetProperty("dom.disable_window_flip")) {
6573 return;
6574 }
6576 // If embedding apps don't implement nsIEmbeddingSiteWindow, we
6577 // shouldn't throw exceptions to web content.
6579 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
6580 nsCOMPtr<nsIEmbeddingSiteWindow> siteWindow(do_GetInterface(treeOwner));
6581 if (siteWindow) {
6582 // This method call may cause mDocShell to become nullptr.
6583 siteWindow->Blur();
6585 // if the root is focused, clear the focus
6586 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6587 if (fm && mDoc) {
6588 nsCOMPtr<nsIDOMElement> element;
6589 fm->GetFocusedElementForWindow(this, false, nullptr, getter_AddRefs(element));
6590 nsCOMPtr<nsIContent> content = do_QueryInterface(element);
6591 if (content == mDoc->GetRootElement())
6592 fm->ClearFocus(this);
6593 }
6594 }
6595 }
6597 NS_IMETHODIMP
6598 nsGlobalWindow::Blur()
6599 {
6600 ErrorResult rv;
6601 Blur(rv);
6603 return rv.ErrorCode();
6604 }
6606 void
6607 nsGlobalWindow::Back(ErrorResult& aError)
6608 {
6609 FORWARD_TO_OUTER_OR_THROW(Back, (aError), aError, );
6611 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6612 if (!webNav) {
6613 aError.Throw(NS_ERROR_FAILURE);
6614 return;
6615 }
6617 aError = webNav->GoBack();
6618 }
6620 NS_IMETHODIMP
6621 nsGlobalWindow::Back()
6622 {
6623 ErrorResult rv;
6624 Back(rv);
6626 return rv.ErrorCode();
6627 }
6629 void
6630 nsGlobalWindow::Forward(ErrorResult& aError)
6631 {
6632 FORWARD_TO_OUTER_OR_THROW(Forward, (aError), aError, );
6634 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6635 if (!webNav) {
6636 aError.Throw(NS_ERROR_FAILURE);
6637 return;
6638 }
6640 aError = webNav->GoForward();
6641 }
6643 NS_IMETHODIMP
6644 nsGlobalWindow::Forward()
6645 {
6646 ErrorResult rv;
6647 Forward(rv);
6649 return rv.ErrorCode();
6650 }
6652 void
6653 nsGlobalWindow::Home(ErrorResult& aError)
6654 {
6655 FORWARD_TO_OUTER_OR_THROW(Home, (aError), aError, );
6657 if (!mDocShell) {
6658 return;
6659 }
6661 nsAdoptingString homeURL =
6662 Preferences::GetLocalizedString(PREF_BROWSER_STARTUP_HOMEPAGE);
6664 if (homeURL.IsEmpty()) {
6665 // if all else fails, use this
6666 #ifdef DEBUG_seth
6667 printf("all else failed. using %s as the home page\n", DEFAULT_HOME_PAGE);
6668 #endif
6669 CopyASCIItoUTF16(DEFAULT_HOME_PAGE, homeURL);
6670 }
6672 #ifdef MOZ_PHOENIX
6673 {
6674 // Firefox lets the user specify multiple home pages to open in
6675 // individual tabs by separating them with '|'. Since we don't
6676 // have the machinery in place to easily open new tabs from here,
6677 // simply truncate the homeURL at the first '|' character to
6678 // prevent any possibilities of leaking the users list of home
6679 // pages to the first home page.
6680 //
6681 // Once bug https://bugzilla.mozilla.org/show_bug.cgi?id=221445 is
6682 // fixed we can revisit this.
6683 int32_t firstPipe = homeURL.FindChar('|');
6685 if (firstPipe > 0) {
6686 homeURL.Truncate(firstPipe);
6687 }
6688 }
6689 #endif
6691 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6692 if (!webNav) {
6693 aError.Throw(NS_ERROR_FAILURE);
6694 return;
6695 }
6697 aError = webNav->LoadURI(homeURL.get(),
6698 nsIWebNavigation::LOAD_FLAGS_NONE,
6699 nullptr,
6700 nullptr,
6701 nullptr);
6702 }
6704 NS_IMETHODIMP
6705 nsGlobalWindow::Home()
6706 {
6707 ErrorResult rv;
6708 Home(rv);
6710 return rv.ErrorCode();
6711 }
6713 void
6714 nsGlobalWindow::Stop(ErrorResult& aError)
6715 {
6716 FORWARD_TO_OUTER_OR_THROW(Stop, (aError), aError, );
6718 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6719 if (webNav) {
6720 aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
6721 }
6722 }
6724 NS_IMETHODIMP
6725 nsGlobalWindow::Stop()
6726 {
6727 ErrorResult rv;
6728 Stop(rv);
6730 return rv.ErrorCode();
6731 }
6733 void
6734 nsGlobalWindow::Print(ErrorResult& aError)
6735 {
6736 #ifdef NS_PRINTING
6737 FORWARD_TO_OUTER_OR_THROW(Print, (aError), aError, );
6739 if (Preferences::GetBool("dom.disable_window_print", false)) {
6740 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6741 return;
6742 }
6744 if (!AreDialogsEnabled()) {
6745 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6746 return;
6747 }
6749 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
6750 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6751 return;
6752 }
6754 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
6755 if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
6756 getter_AddRefs(webBrowserPrint)))) {
6757 nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
6758 GetCurrentInnerWindowInternal()->mDoc :
6759 nullptr);
6761 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
6762 do_GetService("@mozilla.org/gfx/printsettings-service;1");
6764 nsCOMPtr<nsIPrintSettings> printSettings;
6765 if (printSettingsService) {
6766 bool printSettingsAreGlobal =
6767 Preferences::GetBool("print.use_global_printsettings", false);
6769 if (printSettingsAreGlobal) {
6770 printSettingsService->GetGlobalPrintSettings(getter_AddRefs(printSettings));
6772 nsXPIDLString printerName;
6773 printSettings->GetPrinterName(getter_Copies(printerName));
6774 if (printerName.IsEmpty()) {
6775 printSettingsService->GetDefaultPrinterName(getter_Copies(printerName));
6776 printSettings->SetPrinterName(printerName);
6777 }
6778 printSettingsService->InitPrintSettingsFromPrinter(printerName, printSettings);
6779 printSettingsService->InitPrintSettingsFromPrefs(printSettings,
6780 true,
6781 nsIPrintSettings::kInitSaveAll);
6782 } else {
6783 printSettingsService->GetNewPrintSettings(getter_AddRefs(printSettings));
6784 }
6786 EnterModalState();
6787 webBrowserPrint->Print(printSettings, nullptr);
6788 LeaveModalState();
6790 bool savePrintSettings =
6791 Preferences::GetBool("print.save_print_settings", false);
6792 if (printSettingsAreGlobal && savePrintSettings) {
6793 printSettingsService->
6794 SavePrintSettingsToPrefs(printSettings,
6795 true,
6796 nsIPrintSettings::kInitSaveAll);
6797 printSettingsService->
6798 SavePrintSettingsToPrefs(printSettings,
6799 false,
6800 nsIPrintSettings::kInitSavePrinterName);
6801 }
6802 } else {
6803 webBrowserPrint->GetGlobalPrintSettings(getter_AddRefs(printSettings));
6804 webBrowserPrint->Print(printSettings, nullptr);
6805 }
6806 }
6807 #endif //NS_PRINTING
6808 }
6810 NS_IMETHODIMP
6811 nsGlobalWindow::Print()
6812 {
6813 ErrorResult rv;
6814 Print(rv);
6816 return rv.ErrorCode();
6817 }
6819 void
6820 nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos, ErrorResult& aError)
6821 {
6822 FORWARD_TO_OUTER_OR_THROW(MoveTo, (aXPos, aYPos, aError), aError, );
6824 /*
6825 * If caller is not chrome and the user has not explicitly exempted the site,
6826 * prevent window.moveTo() by exiting early
6827 */
6829 if (!CanMoveResizeWindows() || IsFrame()) {
6830 return;
6831 }
6833 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6834 if (!treeOwnerAsWin) {
6835 aError.Throw(NS_ERROR_FAILURE);
6836 return;
6837 }
6839 // Mild abuse of a "size" object so we don't need more helper functions.
6840 nsIntSize cssPos(aXPos, aYPos);
6841 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
6843 nsIntSize devPos = CSSToDevIntPixels(cssPos);
6845 aError = treeOwnerAsWin->SetPosition(devPos.width, devPos.height);
6846 }
6848 NS_IMETHODIMP
6849 nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos)
6850 {
6851 ErrorResult rv;
6852 MoveTo(aXPos, aYPos, rv);
6854 return rv.ErrorCode();
6855 }
6857 void
6858 nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif, ErrorResult& aError)
6859 {
6860 FORWARD_TO_OUTER_OR_THROW(MoveBy, (aXDif, aYDif, aError), aError, );
6862 /*
6863 * If caller is not chrome and the user has not explicitly exempted the site,
6864 * prevent window.moveBy() by exiting early
6865 */
6867 if (!CanMoveResizeWindows() || IsFrame()) {
6868 return;
6869 }
6871 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6872 if (!treeOwnerAsWin) {
6873 aError.Throw(NS_ERROR_FAILURE);
6874 return;
6875 }
6877 // To do this correctly we have to convert what we get from GetPosition
6878 // into CSS pixels, add the arguments, do the security check, and
6879 // then convert back to device pixels for the call to SetPosition.
6881 int32_t x, y;
6882 aError = treeOwnerAsWin->GetPosition(&x, &y);
6883 if (aError.Failed()) {
6884 return;
6885 }
6887 // mild abuse of a "size" object so we don't need more helper functions
6888 nsIntSize cssPos(DevToCSSIntPixels(nsIntSize(x, y)));
6890 cssPos.width += aXDif;
6891 cssPos.height += aYDif;
6893 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
6895 nsIntSize newDevPos(CSSToDevIntPixels(cssPos));
6897 aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height);
6898 }
6900 NS_IMETHODIMP
6901 nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif)
6902 {
6903 ErrorResult rv;
6904 MoveBy(aXDif, aYDif, rv);
6906 return rv.ErrorCode();
6907 }
6909 void
6910 nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight, ErrorResult& aError)
6911 {
6912 FORWARD_TO_OUTER_OR_THROW(ResizeTo, (aWidth, aHeight, aError), aError, );
6914 /*
6915 * If caller is a browser-element then dispatch a resize event to
6916 * the embedder.
6917 */
6918 if (mDocShell && mDocShell->GetIsBrowserOrApp()) {
6919 nsIntSize size(aWidth, aHeight);
6920 if (!DispatchResizeEvent(size)) {
6921 // The embedder chose to prevent the default action for this
6922 // event, so let's not resize this window after all...
6923 return;
6924 }
6925 }
6927 /*
6928 * If caller is not chrome and the user has not explicitly exempted the site,
6929 * prevent window.resizeTo() by exiting early
6930 */
6932 if (!CanMoveResizeWindows() || IsFrame()) {
6933 return;
6934 }
6936 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6937 if (!treeOwnerAsWin) {
6938 aError.Throw(NS_ERROR_FAILURE);
6939 return;
6940 }
6942 nsIntSize cssSize(aWidth, aHeight);
6943 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
6945 nsIntSize devSz(CSSToDevIntPixels(cssSize));
6947 aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true);
6948 }
6950 NS_IMETHODIMP
6951 nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight)
6952 {
6953 ErrorResult rv;
6954 ResizeTo(aWidth, aHeight, rv);
6956 return rv.ErrorCode();
6957 }
6959 void
6960 nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif,
6961 ErrorResult& aError)
6962 {
6963 FORWARD_TO_OUTER_OR_THROW(ResizeBy, (aWidthDif, aHeightDif, aError), aError, );
6965 /*
6966 * If caller is a browser-element then dispatch a resize event to
6967 * parent.
6968 */
6969 if (mDocShell && mDocShell->GetIsBrowserOrApp()) {
6970 CSSIntSize size;
6971 if (NS_FAILED(GetInnerSize(size))) {
6972 return;
6973 }
6975 size.width += aWidthDif;
6976 size.height += aHeightDif;
6978 if (!DispatchResizeEvent(nsIntSize(size.width, size.height))) {
6979 // The embedder chose to prevent the default action for this
6980 // event, so let's not resize this window after all...
6981 return;
6982 }
6983 }
6985 /*
6986 * If caller is not chrome and the user has not explicitly exempted the site,
6987 * prevent window.resizeBy() by exiting early
6988 */
6990 if (!CanMoveResizeWindows() || IsFrame()) {
6991 return;
6992 }
6994 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6995 if (!treeOwnerAsWin) {
6996 aError.Throw(NS_ERROR_FAILURE);
6997 return;
6998 }
7000 int32_t width, height;
7001 aError = treeOwnerAsWin->GetSize(&width, &height);
7002 if (aError.Failed()) {
7003 return;
7004 }
7006 // To do this correctly we have to convert what we got from GetSize
7007 // into CSS pixels, add the arguments, do the security check, and
7008 // then convert back to device pixels for the call to SetSize.
7010 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
7012 cssSize.width += aWidthDif;
7013 cssSize.height += aHeightDif;
7015 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7017 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
7019 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
7020 }
7022 NS_IMETHODIMP
7023 nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif)
7024 {
7025 ErrorResult rv;
7026 ResizeBy(aWidthDif, aHeightDif, rv);
7028 return rv.ErrorCode();
7029 }
7031 void
7032 nsGlobalWindow::SizeToContent(ErrorResult& aError)
7033 {
7034 FORWARD_TO_OUTER_OR_THROW(SizeToContent, (aError), aError, );
7036 if (!mDocShell) {
7037 return;
7038 }
7040 /*
7041 * If caller is not chrome and the user has not explicitly exempted the site,
7042 * prevent window.sizeToContent() by exiting early
7043 */
7045 if (!CanMoveResizeWindows() || IsFrame()) {
7046 return;
7047 }
7049 // The content viewer does a check to make sure that it's a content
7050 // viewer for a toplevel docshell.
7051 nsCOMPtr<nsIContentViewer> cv;
7052 mDocShell->GetContentViewer(getter_AddRefs(cv));
7053 nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(cv));
7054 if (!markupViewer) {
7055 aError.Throw(NS_ERROR_FAILURE);
7056 return;
7057 }
7059 int32_t width, height;
7060 aError = markupViewer->GetContentSize(&width, &height);
7061 if (aError.Failed()) {
7062 return;
7063 }
7065 // Make sure the new size is following the CheckSecurityWidthAndHeight
7066 // rules.
7067 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
7068 if (!treeOwner) {
7069 aError.Throw(NS_ERROR_FAILURE);
7070 return;
7071 }
7073 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
7074 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7076 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
7078 aError = treeOwner->SizeShellTo(mDocShell, newDevSize.width,
7079 newDevSize.height);
7080 }
7082 NS_IMETHODIMP
7083 nsGlobalWindow::SizeToContent()
7084 {
7085 ErrorResult rv;
7086 SizeToContent(rv);
7088 return rv.ErrorCode();
7089 }
7091 NS_IMETHODIMP
7092 nsGlobalWindow::GetWindowRoot(nsIDOMEventTarget **aWindowRoot)
7093 {
7094 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
7095 return CallQueryInterface(root, aWindowRoot);
7096 }
7098 already_AddRefed<nsPIWindowRoot>
7099 nsGlobalWindow::GetTopWindowRoot()
7100 {
7101 nsPIDOMWindow* piWin = GetPrivateRoot();
7102 if (!piWin) {
7103 return nullptr;
7104 }
7106 nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler());
7107 return window.forget();
7108 }
7110 NS_IMETHODIMP
7111 nsGlobalWindow::Scroll(int32_t aXScroll, int32_t aYScroll)
7112 {
7113 ScrollTo(CSSIntPoint(aXScroll, aYScroll));
7114 return NS_OK;
7115 }
7117 NS_IMETHODIMP
7118 nsGlobalWindow::ScrollTo(int32_t aXScroll, int32_t aYScroll)
7119 {
7120 ScrollTo(CSSIntPoint(aXScroll, aYScroll));
7121 return NS_OK;
7122 }
7124 void
7125 nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll)
7126 {
7127 FlushPendingNotifications(Flush_Layout);
7128 nsIScrollableFrame *sf = GetScrollFrame();
7130 if (sf) {
7131 // Here we calculate what the max pixel value is that we can
7132 // scroll to, we do this by dividing maxint with the pixel to
7133 // twips conversion factor, and subtracting 4, the 4 comes from
7134 // experimenting with this value, anything less makes the view
7135 // code not scroll correctly, I have no idea why. -- jst
7136 const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4;
7138 CSSIntPoint scroll(aScroll);
7139 if (scroll.x > maxpx) {
7140 scroll.x = maxpx;
7141 }
7143 if (scroll.y > maxpx) {
7144 scroll.y = maxpx;
7145 }
7146 sf->ScrollToCSSPixels(scroll);
7147 }
7148 }
7150 NS_IMETHODIMP
7151 nsGlobalWindow::ScrollBy(int32_t aXScrollDif, int32_t aYScrollDif)
7152 {
7153 FlushPendingNotifications(Flush_Layout);
7154 nsIScrollableFrame *sf = GetScrollFrame();
7156 if (sf) {
7157 CSSIntPoint scrollPos =
7158 sf->GetScrollPositionCSSPixels() + CSSIntPoint(aXScrollDif, aYScrollDif);
7159 // It seems like it would make more sense for ScrollBy to use
7160 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
7161 // Perhaps Web content does too.
7162 ScrollTo(scrollPos);
7163 }
7165 return NS_OK;
7166 }
7168 NS_IMETHODIMP
7169 nsGlobalWindow::ScrollByLines(int32_t numLines)
7170 {
7171 FlushPendingNotifications(Flush_Layout);
7172 nsIScrollableFrame *sf = GetScrollFrame();
7173 if (sf) {
7174 // It seems like it would make more sense for ScrollByLines to use
7175 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
7176 // Perhaps Web content does too.
7177 sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES,
7178 nsIScrollableFrame::INSTANT);
7179 }
7181 return NS_OK;
7182 }
7184 NS_IMETHODIMP
7185 nsGlobalWindow::ScrollByPages(int32_t numPages)
7186 {
7187 FlushPendingNotifications(Flush_Layout);
7188 nsIScrollableFrame *sf = GetScrollFrame();
7189 if (sf) {
7190 // It seems like it would make more sense for ScrollByPages to use
7191 // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
7192 // Perhaps Web content does too.
7193 sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES,
7194 nsIScrollableFrame::INSTANT);
7195 }
7197 return NS_OK;
7198 }
7200 void
7201 nsGlobalWindow::ClearTimeout(int32_t aHandle, ErrorResult& aError)
7202 {
7203 if (aHandle > 0) {
7204 ClearTimeoutOrInterval(aHandle, aError);
7205 }
7206 }
7208 NS_IMETHODIMP
7209 nsGlobalWindow::ClearTimeout(int32_t aHandle)
7210 {
7211 ErrorResult rv;
7212 ClearTimeout(aHandle, rv);
7214 return rv.ErrorCode();
7215 }
7217 void
7218 nsGlobalWindow::ClearInterval(int32_t aHandle, ErrorResult& aError)
7219 {
7220 if (aHandle > 0) {
7221 ClearTimeoutOrInterval(aHandle, aError);
7222 }
7223 }
7225 NS_IMETHODIMP
7226 nsGlobalWindow::ClearInterval(int32_t aHandle)
7227 {
7228 ErrorResult rv;
7229 ClearInterval(aHandle, rv);
7231 return rv.ErrorCode();
7232 }
7234 NS_IMETHODIMP
7235 nsGlobalWindow::SetTimeout(int32_t *_retval)
7236 {
7237 return SetTimeoutOrInterval(false, _retval);
7238 }
7240 NS_IMETHODIMP
7241 nsGlobalWindow::SetInterval(int32_t *_retval)
7242 {
7243 return SetTimeoutOrInterval(true, _retval);
7244 }
7246 NS_IMETHODIMP
7247 nsGlobalWindow::SetResizable(bool aResizable)
7248 {
7249 // nop
7251 return NS_OK;
7252 }
7254 NS_IMETHODIMP
7255 nsGlobalWindow::CaptureEvents()
7256 {
7257 if (mDoc) {
7258 mDoc->WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
7259 }
7261 return NS_OK;
7262 }
7264 NS_IMETHODIMP
7265 nsGlobalWindow::ReleaseEvents()
7266 {
7267 if (mDoc) {
7268 mDoc->WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
7269 }
7271 return NS_OK;
7272 }
7274 static
7275 bool IsPopupBlocked(nsIDocument* aDoc)
7276 {
7277 nsCOMPtr<nsIPopupWindowManager> pm =
7278 do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
7280 if (!pm) {
7281 return false;
7282 }
7284 if (!aDoc) {
7285 return true;
7286 }
7288 uint32_t permission = nsIPopupWindowManager::ALLOW_POPUP;
7289 pm->TestPermission(aDoc->NodePrincipal(), &permission);
7290 return permission == nsIPopupWindowManager::DENY_POPUP;
7291 }
7293 /* static */
7294 void
7295 nsGlobalWindow::FirePopupBlockedEvent(nsIDocument* aDoc,
7296 nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI,
7297 const nsAString &aPopupWindowName,
7298 const nsAString &aPopupWindowFeatures)
7299 {
7300 if (aDoc) {
7301 // Fire a "DOMPopupBlocked" event so that the UI can hear about
7302 // blocked popups.
7303 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDoc);
7304 nsCOMPtr<nsIDOMEvent> event;
7305 doc->CreateEvent(NS_LITERAL_STRING("PopupBlockedEvents"), getter_AddRefs(event));
7306 if (event) {
7307 nsCOMPtr<nsIDOMPopupBlockedEvent> pbev(do_QueryInterface(event));
7308 pbev->InitPopupBlockedEvent(NS_LITERAL_STRING("DOMPopupBlocked"),
7309 true, true, aRequestingWindow,
7310 aPopupURI, aPopupWindowName,
7311 aPopupWindowFeatures);
7312 event->SetTrusted(true);
7314 bool defaultActionEnabled;
7315 aDoc->DispatchEvent(event, &defaultActionEnabled);
7316 }
7317 }
7318 }
7320 static void FirePopupWindowEvent(nsIDocument* aDoc)
7321 {
7322 // Fire a "PopupWindow" event
7323 nsContentUtils::DispatchTrustedEvent(aDoc, aDoc,
7324 NS_LITERAL_STRING("PopupWindow"),
7325 true, true);
7326 }
7328 // static
7329 bool
7330 nsGlobalWindow::CanSetProperty(const char *aPrefName)
7331 {
7332 // Chrome can set any property.
7333 if (nsContentUtils::IsCallerChrome()) {
7334 return true;
7335 }
7337 // If the pref is set to true, we can not set the property
7338 // and vice versa.
7339 return !Preferences::GetBool(aPrefName, true);
7340 }
7342 bool
7343 nsGlobalWindow::PopupWhitelisted()
7344 {
7345 if (!IsPopupBlocked(mDoc))
7346 return true;
7348 nsCOMPtr<nsIDOMWindow> parent;
7350 if (NS_FAILED(GetParent(getter_AddRefs(parent))) ||
7351 parent == static_cast<nsIDOMWindow*>(this))
7352 {
7353 return false;
7354 }
7356 return static_cast<nsGlobalWindow*>
7357 (static_cast<nsIDOMWindow*>
7358 (parent.get()))->PopupWhitelisted();
7359 }
7361 /*
7362 * Examine the current document state to see if we're in a way that is
7363 * typically abused by web designers. The window.open code uses this
7364 * routine to determine whether to allow the new window.
7365 * Returns a value from the PopupControlState enum.
7366 */
7367 PopupControlState
7368 nsGlobalWindow::RevisePopupAbuseLevel(PopupControlState aControl)
7369 {
7370 MOZ_ASSERT(IsOuterWindow());
7372 NS_ASSERTION(mDocShell, "Must have docshell");
7374 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
7375 return openAllowed;
7376 }
7378 PopupControlState abuse = aControl;
7379 switch (abuse) {
7380 case openControlled:
7381 case openAbused:
7382 case openOverridden:
7383 if (PopupWhitelisted())
7384 abuse = PopupControlState(abuse - 1);
7385 case openAllowed: break;
7386 default:
7387 NS_WARNING("Strange PopupControlState!");
7388 }
7390 // limit the number of simultaneously open popups
7391 if (abuse == openAbused || abuse == openControlled) {
7392 int32_t popupMax = Preferences::GetInt("dom.popup_maximum", -1);
7393 if (popupMax >= 0 && gOpenPopupSpamCount >= popupMax)
7394 abuse = openOverridden;
7395 }
7397 return abuse;
7398 }
7400 /* If a window open is blocked, fire the appropriate DOM events.
7401 aBlocked signifies we just blocked a popup.
7402 aWindow signifies we just opened what is probably a popup.
7403 */
7404 void
7405 nsGlobalWindow::FireAbuseEvents(bool aBlocked, bool aWindow,
7406 const nsAString &aPopupURL,
7407 const nsAString &aPopupWindowName,
7408 const nsAString &aPopupWindowFeatures)
7409 {
7410 // fetch the URI of the window requesting the opened window
7412 nsCOMPtr<nsIDOMWindow> topWindow;
7413 GetTop(getter_AddRefs(topWindow));
7414 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(topWindow);
7415 if (!window) {
7416 return;
7417 }
7419 nsCOMPtr<nsIDocument> topDoc = window->GetDoc();
7420 nsCOMPtr<nsIURI> popupURI;
7422 // build the URI of the would-have-been popup window
7423 // (see nsWindowWatcher::URIfromURL)
7425 // first, fetch the opener's base URI
7427 nsIURI *baseURL = nullptr;
7429 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7430 nsCOMPtr<nsPIDOMWindow> contextWindow;
7432 if (cx) {
7433 nsIScriptContext *currentCX = nsJSUtils::GetDynamicScriptContext(cx);
7434 if (currentCX) {
7435 contextWindow = do_QueryInterface(currentCX->GetGlobalObject());
7436 }
7437 }
7438 if (!contextWindow) {
7439 contextWindow = this;
7440 }
7442 nsCOMPtr<nsIDocument> doc = contextWindow->GetDoc();
7443 if (doc)
7444 baseURL = doc->GetDocBaseURI();
7446 // use the base URI to build what would have been the popup's URI
7447 nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
7448 if (ios)
7449 ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), 0, baseURL,
7450 getter_AddRefs(popupURI));
7452 // fire an event chock full of informative URIs
7453 if (aBlocked) {
7454 FirePopupBlockedEvent(topDoc, this, popupURI, aPopupWindowName,
7455 aPopupWindowFeatures);
7456 }
7457 if (aWindow)
7458 FirePopupWindowEvent(topDoc);
7459 }
7461 already_AddRefed<nsIDOMWindow>
7462 nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
7463 const nsAString& aOptions, ErrorResult& aError)
7464 {
7465 nsCOMPtr<nsIDOMWindow> window;
7466 aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window));
7467 return window.forget();
7468 }
7470 NS_IMETHODIMP
7471 nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
7472 const nsAString& aOptions, nsIDOMWindow **_retval)
7473 {
7474 return OpenInternal(aUrl, aName, aOptions,
7475 false, // aDialog
7476 false, // aContentModal
7477 true, // aCalledNoScript
7478 false, // aDoJSFixups
7479 true, // aNavigate
7480 nullptr, nullptr, // No args
7481 GetPrincipal(), // aCalleePrincipal
7482 nullptr, // aJSCallerContext
7483 _retval);
7484 }
7486 NS_IMETHODIMP
7487 nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName,
7488 const nsAString& aOptions, nsIDOMWindow **_retval)
7489 {
7490 return OpenInternal(aUrl, aName, aOptions,
7491 false, // aDialog
7492 false, // aContentModal
7493 false, // aCalledNoScript
7494 true, // aDoJSFixups
7495 true, // aNavigate
7496 nullptr, nullptr, // No args
7497 GetPrincipal(), // aCalleePrincipal
7498 nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
7499 _retval);
7500 }
7502 // like Open, but attaches to the new window any extra parameters past
7503 // [features] as a JS property named "arguments"
7504 NS_IMETHODIMP
7505 nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
7506 const nsAString& aOptions,
7507 nsISupports* aExtraArgument, nsIDOMWindow** _retval)
7508 {
7509 return OpenInternal(aUrl, aName, aOptions,
7510 true, // aDialog
7511 false, // aContentModal
7512 true, // aCalledNoScript
7513 false, // aDoJSFixups
7514 true, // aNavigate
7515 nullptr, aExtraArgument, // Arguments
7516 GetPrincipal(), // aCalleePrincipal
7517 nullptr, // aJSCallerContext
7518 _retval);
7519 }
7521 // Like Open, but passes aNavigate=false.
7522 /* virtual */ nsresult
7523 nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
7524 const nsAString& aName,
7525 const nsAString& aOptions,
7526 nsIDOMWindow **_retval)
7527 {
7528 return OpenInternal(aUrl, aName, aOptions,
7529 false, // aDialog
7530 false, // aContentModal
7531 true, // aCalledNoScript
7532 false, // aDoJSFixups
7533 false, // aNavigate
7534 nullptr, nullptr, // No args
7535 GetPrincipal(), // aCalleePrincipal
7536 nullptr, // aJSCallerContext
7537 _retval);
7539 }
7541 already_AddRefed<nsIDOMWindow>
7542 nsGlobalWindow::OpenDialog(JSContext* aCx, const nsAString& aUrl,
7543 const nsAString& aName, const nsAString& aOptions,
7544 const Sequence<JS::Value>& aExtraArgument,
7545 ErrorResult& aError)
7546 {
7547 nsCOMPtr<nsIJSArgArray> argvArray;
7548 aError = NS_CreateJSArgv(aCx, aExtraArgument.Length(),
7549 const_cast<JS::Value*>(aExtraArgument.Elements()),
7550 getter_AddRefs(argvArray));
7551 if (aError.Failed()) {
7552 return nullptr;
7553 }
7555 nsCOMPtr<nsIDOMWindow> dialog;
7556 aError = OpenInternal(aUrl, aName, aOptions,
7557 true, // aDialog
7558 false, // aContentModal
7559 false, // aCalledNoScript
7560 false, // aDoJSFixups
7561 true, // aNavigate
7562 argvArray, nullptr, // Arguments
7563 GetPrincipal(), // aCalleePrincipal
7564 aCx, // aJSCallerContext
7565 getter_AddRefs(dialog));
7566 return dialog.forget();
7567 }
7569 NS_IMETHODIMP
7570 nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
7571 const nsAString& aOptions, nsIDOMWindow** _retval)
7572 {
7573 if (!nsContentUtils::IsCallerChrome()) {
7574 return NS_ERROR_DOM_SECURITY_ERR;
7575 }
7577 nsAXPCNativeCallContext *ncc = nullptr;
7578 nsresult rv = nsContentUtils::XPConnect()->
7579 GetCurrentNativeCallContext(&ncc);
7580 NS_ENSURE_SUCCESS(rv, rv);
7582 if (!ncc)
7583 return NS_ERROR_NOT_AVAILABLE;
7585 JSContext *cx = nullptr;
7587 rv = ncc->GetJSContext(&cx);
7588 NS_ENSURE_SUCCESS(rv, rv);
7590 uint32_t argc;
7591 JS::Value *argv = nullptr;
7593 // XXX - need to get this as nsISupports?
7594 ncc->GetArgc(&argc);
7595 ncc->GetArgvPtr(&argv);
7597 // Strip the url, name and options from the args seen by scripts.
7598 uint32_t argOffset = argc < 3 ? argc : 3;
7599 nsCOMPtr<nsIJSArgArray> argvArray;
7600 rv = NS_CreateJSArgv(cx, argc - argOffset, argv + argOffset,
7601 getter_AddRefs(argvArray));
7602 NS_ENSURE_SUCCESS(rv, rv);
7604 return OpenInternal(aUrl, aName, aOptions,
7605 true, // aDialog
7606 false, // aContentModal
7607 false, // aCalledNoScript
7608 false, // aDoJSFixups
7609 true, // aNavigate
7610 argvArray, nullptr, // Arguments
7611 GetPrincipal(), // aCalleePrincipal
7612 cx, // aJSCallerContext
7613 _retval);
7614 }
7616 already_AddRefed<nsIDOMWindow>
7617 nsGlobalWindow::GetFrames(ErrorResult& aError)
7618 {
7619 FORWARD_TO_OUTER_OR_THROW(GetFrames, (aError), aError, nullptr);
7621 nsRefPtr<nsGlobalWindow> frames(this);
7622 FlushPendingNotifications(Flush_ContentAndNotify);
7623 return frames.forget();
7624 }
7626 NS_IMETHODIMP
7627 nsGlobalWindow::GetFrames(nsIDOMWindow** aFrames)
7628 {
7629 ErrorResult rv;
7630 nsCOMPtr<nsIDOMWindow> frames = GetFrames(rv);
7631 frames.forget(aFrames);
7633 return rv.ErrorCode();
7634 }
7636 JSObject* nsGlobalWindow::CallerGlobal()
7637 {
7638 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7639 if (!cx) {
7640 NS_ERROR("Please don't call this method from C++!");
7642 return nullptr;
7643 }
7645 // If somebody does sameOriginIframeWindow.postMessage(...), they probably
7646 // expect the .source attribute of the resulting message event to be |window|
7647 // rather than |sameOriginIframeWindow|, even though the transparent wrapper
7648 // semantics of same-origin access will cause us to be in the iframe's
7649 // compartment at the time of the call. This means that we want the incumbent
7650 // global here, rather than the global of the current compartment.
7651 //
7652 // There are various edge cases in which the incumbent global and the current
7653 // global would not be same-origin. They include:
7654 // * A privileged caller (System Principal or Expanded Principal) manipulating
7655 // less-privileged content via Xray Waivers.
7656 // * An unprivileged caller invoking a cross-origin function that was exposed
7657 // to it by privileged code (i.e. Sandbox.importFunction).
7658 //
7659 // In these cases, we probably don't want the privileged global appearing in the
7660 // .source attribute. So we fall back to the compartment global there.
7661 JS::Rooted<JSObject*> incumbentGlobal(cx, &IncumbentJSGlobal());
7662 JS::Rooted<JSObject*> compartmentGlobal(cx, JS::CurrentGlobalOrNull(cx));
7663 nsIPrincipal* incumbentPrin = nsContentUtils::GetObjectPrincipal(incumbentGlobal);
7664 nsIPrincipal* compartmentPrin = nsContentUtils::GetObjectPrincipal(compartmentGlobal);
7665 return incumbentPrin->EqualsConsideringDomain(compartmentPrin) ? incumbentGlobal
7666 : compartmentGlobal;
7667 }
7670 nsGlobalWindow*
7671 nsGlobalWindow::CallerInnerWindow()
7672 {
7673 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7674 NS_ENSURE_TRUE(cx, nullptr);
7675 JS::Rooted<JSObject*> scope(cx, CallerGlobal());
7677 // When Jetpack runs content scripts inside a sandbox, it uses
7678 // sandboxPrototype to make them appear as though they're running in the
7679 // scope of the page. So when a content script invokes postMessage, it expects
7680 // the |source| of the received message to be the window set as the
7681 // sandboxPrototype. This used to work incidentally for unrelated reasons, but
7682 // now we need to do some special handling to support it.
7683 {
7684 JSAutoCompartment ac(cx, scope);
7685 JS::Rooted<JSObject*> scopeProto(cx);
7686 bool ok = JS_GetPrototype(cx, scope, &scopeProto);
7687 NS_ENSURE_TRUE(ok, nullptr);
7688 if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) &&
7689 (scopeProto = js::CheckedUnwrap(scopeProto, /* stopAtOuter = */ false)))
7690 {
7691 scope = scopeProto;
7692 }
7693 }
7694 JSAutoCompartment ac(cx, scope);
7696 // We don't use xpc::WindowOrNull here because we want to be able to tell
7697 // apart the cases of "scope is not an nsISupports at all" and "scope is an
7698 // nsISupports that's not a window". It's not clear whether that's desirable,
7699 // see bug 984467.
7700 nsISupports* native =
7701 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, scope);
7702 if (!native)
7703 return nullptr;
7705 // The calling window must be holding a reference, so we can just return a
7706 // raw pointer here and let the QI's addref be balanced by the nsCOMPtr
7707 // destructor's release.
7708 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(native);
7709 if (!win)
7710 return GetCurrentInnerWindowInternal();
7711 return static_cast<nsGlobalWindow*>(win.get());
7712 }
7714 nsresult
7715 nsGlobalWindow::GetFirstPartyIsolationURI(nsIURI** aFirstPartyIsolationURI)
7716 {
7717 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
7718 do_GetService(THIRDPARTYUTIL_CONTRACTID);
7719 if (!thirdPartyUtil)
7720 return NS_ERROR_FAILURE;
7722 nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDoc);
7723 return thirdPartyUtil->GetFirstPartyIsolationURI(NULL, doc, aFirstPartyIsolationURI);
7724 }
7727 /**
7728 * Class used to represent events generated by calls to Window.postMessage,
7729 * which asynchronously creates and dispatches events.
7730 */
7731 class PostMessageEvent : public nsRunnable
7732 {
7733 public:
7734 NS_DECL_NSIRUNNABLE
7736 PostMessageEvent(nsGlobalWindow* aSource,
7737 const nsAString& aCallerOrigin,
7738 nsGlobalWindow* aTargetWindow,
7739 nsIPrincipal* aProvidedPrincipal,
7740 bool aTrustedCaller)
7741 : mSource(aSource),
7742 mCallerOrigin(aCallerOrigin),
7743 mTargetWindow(aTargetWindow),
7744 mProvidedPrincipal(aProvidedPrincipal),
7745 mTrustedCaller(aTrustedCaller)
7746 {
7747 MOZ_COUNT_CTOR(PostMessageEvent);
7748 }
7750 ~PostMessageEvent()
7751 {
7752 MOZ_COUNT_DTOR(PostMessageEvent);
7753 }
7755 JSAutoStructuredCloneBuffer& Buffer()
7756 {
7757 return mBuffer;
7758 }
7760 bool StoreISupports(nsISupports* aSupports)
7761 {
7762 mSupportsArray.AppendElement(aSupports);
7763 return true;
7764 }
7766 private:
7767 JSAutoStructuredCloneBuffer mBuffer;
7768 nsRefPtr<nsGlobalWindow> mSource;
7769 nsString mCallerOrigin;
7770 nsRefPtr<nsGlobalWindow> mTargetWindow;
7771 nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
7772 bool mTrustedCaller;
7773 nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
7774 };
7776 namespace {
7778 struct StructuredCloneInfo {
7779 PostMessageEvent* event;
7780 bool subsumes;
7781 nsPIDOMWindow* window;
7782 nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
7783 };
7785 static JSObject*
7786 PostMessageReadStructuredClone(JSContext* cx,
7787 JSStructuredCloneReader* reader,
7788 uint32_t tag,
7789 uint32_t data,
7790 void* closure)
7791 {
7792 if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
7793 NS_ASSERTION(!data, "Data should be empty");
7795 nsISupports* supports;
7796 if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
7797 JS::Rooted<JS::Value> val(cx);
7798 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
7799 return val.toObjectOrNull();
7800 }
7801 }
7802 }
7804 const JSStructuredCloneCallbacks* runtimeCallbacks =
7805 js::GetContextStructuredCloneCallbacks(cx);
7807 if (runtimeCallbacks) {
7808 return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
7809 }
7811 return nullptr;
7812 }
7814 static bool
7815 PostMessageWriteStructuredClone(JSContext* cx,
7816 JSStructuredCloneWriter* writer,
7817 JS::Handle<JSObject*> obj,
7818 void *closure)
7819 {
7820 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
7821 NS_ASSERTION(scInfo, "Must have scInfo!");
7823 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
7824 nsContentUtils::XPConnect()->
7825 GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
7826 if (wrappedNative) {
7827 uint32_t scTag = 0;
7828 nsISupports* supports = wrappedNative->Native();
7830 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
7831 if (blob && scInfo->subsumes)
7832 scTag = SCTAG_DOM_BLOB;
7834 nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
7835 if (list && scInfo->subsumes)
7836 scTag = SCTAG_DOM_FILELIST;
7838 if (scTag)
7839 return JS_WriteUint32Pair(writer, scTag, 0) &&
7840 JS_WriteBytes(writer, &supports, sizeof(supports)) &&
7841 scInfo->event->StoreISupports(supports);
7842 }
7844 const JSStructuredCloneCallbacks* runtimeCallbacks =
7845 js::GetContextStructuredCloneCallbacks(cx);
7847 if (runtimeCallbacks) {
7848 return runtimeCallbacks->write(cx, writer, obj, nullptr);
7849 }
7851 return false;
7852 }
7854 static bool
7855 PostMessageReadTransferStructuredClone(JSContext* aCx,
7856 JSStructuredCloneReader* reader,
7857 uint32_t tag, void* aData,
7858 uint64_t aExtraData,
7859 void* aClosure,
7860 JS::MutableHandle<JSObject*> returnObject)
7861 {
7862 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
7863 NS_ASSERTION(scInfo, "Must have scInfo!");
7865 if (MessageChannel::PrefEnabled() && tag == SCTAG_DOM_MAP_MESSAGEPORT) {
7866 MessagePort* port = static_cast<MessagePort*>(aData);
7867 port->BindToOwner(scInfo->window);
7868 scInfo->ports.Put(port, nullptr);
7870 JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx));
7871 if (JS_WrapObject(aCx, &obj)) {
7872 MOZ_ASSERT(port->GetOwner() == scInfo->window);
7873 returnObject.set(obj);
7874 }
7876 return true;
7877 }
7879 return false;
7880 }
7882 static bool
7883 PostMessageTransferStructuredClone(JSContext* aCx,
7884 JS::Handle<JSObject*> aObj,
7885 void* aClosure,
7886 uint32_t* aTag,
7887 JS::TransferableOwnership* aOwnership,
7888 void** aContent,
7889 uint64_t* aExtraData)
7890 {
7891 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
7892 NS_ASSERTION(scInfo, "Must have scInfo!");
7894 if (MessageChannel::PrefEnabled()) {
7895 MessagePortBase* port = nullptr;
7896 nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
7897 if (NS_SUCCEEDED(rv)) {
7898 nsRefPtr<MessagePortBase> newPort;
7899 if (scInfo->ports.Get(port, getter_AddRefs(newPort))) {
7900 // No duplicate.
7901 return false;
7902 }
7904 newPort = port->Clone();
7905 scInfo->ports.Put(port, newPort);
7907 *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
7908 *aOwnership = JS::SCTAG_TMO_CUSTOM;
7909 *aContent = newPort;
7910 *aExtraData = 0;
7912 return true;
7913 }
7914 }
7916 return false;
7917 }
7919 void
7920 PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
7921 void *aContent, uint64_t aExtraData, void* aClosure)
7922 {
7923 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
7924 NS_ASSERTION(scInfo, "Must have scInfo!");
7926 if (MessageChannel::PrefEnabled() && aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
7927 nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
7928 scInfo->ports.Remove(port);
7929 }
7930 }
7932 JSStructuredCloneCallbacks kPostMessageCallbacks = {
7933 PostMessageReadStructuredClone,
7934 PostMessageWriteStructuredClone,
7935 nullptr,
7936 PostMessageReadTransferStructuredClone,
7937 PostMessageTransferStructuredClone,
7938 PostMessageFreeTransferStructuredClone
7939 };
7941 } // anonymous namespace
7943 static PLDHashOperator
7944 PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
7945 {
7946 nsTArray<nsRefPtr<MessagePortBase> > *array =
7947 static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
7949 array->AppendElement(aKey);
7950 return PL_DHASH_NEXT;
7951 }
7953 NS_IMETHODIMP
7954 PostMessageEvent::Run()
7955 {
7956 NS_ABORT_IF_FALSE(mTargetWindow->IsOuterWindow(),
7957 "should have been passed an outer window!");
7958 NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(),
7959 "should have been passed an outer window!");
7961 AutoJSAPI jsapi;
7962 JSContext* cx = jsapi.cx();
7964 // If we bailed before this point we're going to leak mMessage, but
7965 // that's probably better than crashing.
7967 nsRefPtr<nsGlobalWindow> targetWindow;
7968 if (mTargetWindow->IsClosedOrClosing() ||
7969 !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
7970 targetWindow->IsClosedOrClosing())
7971 return NS_OK;
7973 NS_ABORT_IF_FALSE(targetWindow->IsInnerWindow(),
7974 "we ordered an inner window!");
7975 JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
7977 // Ensure that any origin which might have been provided is the origin of this
7978 // window's document. Note that we do this *now* instead of when postMessage
7979 // is called because the target window might have been navigated to a
7980 // different location between then and now. If this check happened when
7981 // postMessage was called, it would be fairly easy for a malicious webpage to
7982 // intercept messages intended for another site by carefully timing navigation
7983 // of the target window so it changed location after postMessage but before
7984 // now.
7985 if (mProvidedPrincipal) {
7986 // Get the target's origin either from its principal or, in the case the
7987 // principal doesn't carry a URI (e.g. the system principal), the target's
7988 // document.
7989 nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
7990 if (NS_WARN_IF(!targetPrin))
7991 return NS_OK;
7993 // Note: This is contrary to the spec with respect to file: URLs, which
7994 // the spec groups into a single origin, but given we intentionally
7995 // don't do that in other places it seems better to hold the line for
7996 // now. Long-term, we want HTML5 to address this so that we can
7997 // be compliant while being safer.
7998 if (!targetPrin->Equals(mProvidedPrincipal)) {
7999 return NS_OK;
8000 }
8001 }
8003 // Deserialize the structured clone data
8004 JS::Rooted<JS::Value> messageData(cx);
8005 StructuredCloneInfo scInfo;
8006 scInfo.event = this;
8007 scInfo.window = targetWindow;
8009 if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
8010 return NS_ERROR_DOM_DATA_CLONE_ERR;
8011 }
8013 // Create the event
8014 nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
8015 do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get()));
8016 nsRefPtr<MessageEvent> event =
8017 new MessageEvent(eventTarget, nullptr, nullptr);
8019 event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
8020 false /*cancelable */, messageData, mCallerOrigin,
8021 EmptyString(), mSource);
8023 nsTArray<nsRefPtr<MessagePortBase> > ports;
8024 scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
8025 event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
8027 // We can't simply call dispatchEvent on the window because doing so ends
8028 // up flipping the trusted bit on the event, and we don't want that to
8029 // happen because then untrusted content can call postMessage on a chrome
8030 // window if it can get a reference to it.
8032 nsIPresShell *shell = targetWindow->mDoc->GetShell();
8033 nsRefPtr<nsPresContext> presContext;
8034 if (shell)
8035 presContext = shell->GetPresContext();
8037 event->SetTrusted(mTrustedCaller);
8038 WidgetEvent* internalEvent = event->GetInternalNSEvent();
8040 nsEventStatus status = nsEventStatus_eIgnore;
8041 EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow),
8042 presContext,
8043 internalEvent,
8044 static_cast<dom::Event*>(event.get()),
8045 &status);
8046 return NS_OK;
8047 }
8049 void
8050 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
8051 const nsAString& aTargetOrigin,
8052 JS::Handle<JS::Value> aTransfer,
8053 ErrorResult& aError)
8054 {
8055 FORWARD_TO_OUTER_OR_THROW(PostMessageMoz,
8056 (aCx, aMessage, aTargetOrigin, aTransfer, aError),
8057 aError, );
8059 //
8060 // Window.postMessage is an intentional subversion of the same-origin policy.
8061 // As such, this code must be particularly careful in the information it
8062 // exposes to calling code.
8063 //
8064 // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
8065 //
8067 // First, get the caller's window
8068 nsRefPtr<nsGlobalWindow> callerInnerWin = CallerInnerWindow();
8069 nsIPrincipal* callerPrin;
8070 if (callerInnerWin) {
8071 NS_ABORT_IF_FALSE(callerInnerWin->IsInnerWindow(),
8072 "should have gotten an inner window here");
8074 // Compute the caller's origin either from its principal or, in the case the
8075 // principal doesn't carry a URI (e.g. the system principal), the caller's
8076 // document. We must get this now instead of when the event is created and
8077 // dispatched, because ultimately it is the identity of the calling window
8078 // *now* that determines who sent the message (and not an identity which might
8079 // have changed due to intervening navigations).
8080 callerPrin = callerInnerWin->GetPrincipal();
8081 }
8082 else {
8083 // In case the global is not a window, it can be a sandbox, and the sandbox's
8084 // principal can be used for the security check.
8085 JSObject *global = CallerGlobal();
8086 NS_ASSERTION(global, "Why is there no global object?");
8087 JSCompartment *compartment = js::GetObjectCompartment(global);
8088 callerPrin = xpc::GetCompartmentPrincipal(compartment);
8089 }
8090 if (!callerPrin) {
8091 return;
8092 }
8094 nsCOMPtr<nsIURI> callerOuterURI;
8095 if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) {
8096 return;
8097 }
8099 nsAutoString origin;
8100 if (callerOuterURI) {
8101 // if the principal has a URI, use that to generate the origin
8102 nsContentUtils::GetUTFOrigin(callerPrin, origin);
8103 }
8104 else if (callerInnerWin) {
8105 // otherwise use the URI of the document to generate origin
8106 nsCOMPtr<nsIDocument> doc = callerInnerWin->GetExtantDoc();
8107 if (!doc) {
8108 return;
8109 }
8110 callerOuterURI = doc->GetDocumentURI();
8111 // if the principal has a URI, use that to generate the origin
8112 nsContentUtils::GetUTFOrigin(callerOuterURI, origin);
8113 }
8114 else {
8115 // in case of a sandbox with a system principal origin can be empty
8116 if (!nsContentUtils::IsSystemPrincipal(callerPrin)) {
8117 return;
8118 }
8119 }
8121 // Convert the provided origin string into a URI for comparison purposes.
8122 nsCOMPtr<nsIPrincipal> providedPrincipal;
8124 if (aTargetOrigin.EqualsASCII("/")) {
8125 providedPrincipal = BrokenGetEntryGlobal()->PrincipalOrNull();
8126 if (NS_WARN_IF(!providedPrincipal))
8127 return;
8128 }
8130 // "*" indicates no specific origin is required.
8131 else if (!aTargetOrigin.EqualsASCII("*")) {
8132 nsCOMPtr<nsIURI> originURI;
8133 if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
8134 aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
8135 return;
8136 }
8138 if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
8139 NS_FAILED(originURI->SetPath(EmptyCString()))) {
8140 return;
8141 }
8143 nsCOMPtr<nsIScriptSecurityManager> ssm =
8144 nsContentUtils::GetSecurityManager();
8145 MOZ_ASSERT(ssm);
8147 nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSubjectPrincipal();
8148 MOZ_ASSERT(principal);
8150 uint32_t appId;
8151 if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId))))
8152 return;
8154 bool isInBrowser;
8155 if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser))))
8156 return;
8158 // Create a nsIPrincipal inheriting the app/browser attributes from the
8159 // caller.
8160 nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
8161 getter_AddRefs(providedPrincipal));
8162 if (NS_WARN_IF(NS_FAILED(rv))) {
8163 return;
8164 }
8165 }
8167 // Create and asynchronously dispatch a runnable which will handle actual DOM
8168 // event creation and dispatch.
8169 nsRefPtr<PostMessageEvent> event =
8170 new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
8171 ? nullptr
8172 : callerInnerWin->GetOuterWindowInternal(),
8173 origin,
8174 this,
8175 providedPrincipal,
8176 nsContentUtils::IsCallerChrome());
8178 // We *must* clone the data here, or the JS::Value could be modified
8179 // by script
8180 StructuredCloneInfo scInfo;
8181 scInfo.event = event;
8182 scInfo.window = this;
8184 nsIPrincipal* principal = GetPrincipal();
8185 JS::Rooted<JS::Value> message(aCx, aMessage);
8186 JS::Rooted<JS::Value> transfer(aCx, aTransfer);
8187 if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)) ||
8188 !event->Buffer().write(aCx, message, transfer, &kPostMessageCallbacks,
8189 &scInfo)) {
8190 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
8191 return;
8192 }
8194 aError = NS_DispatchToCurrentThread(event);
8195 }
8197 void
8198 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
8199 const nsAString& aTargetOrigin,
8200 const Optional<Sequence<JS::Value > >& aTransfer,
8201 ErrorResult& aError)
8202 {
8203 JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
8204 if (aTransfer.WasPassed()) {
8205 const Sequence<JS::Value >& values = aTransfer.Value();
8207 // The input sequence only comes from the generated bindings code, which
8208 // ensures it is rooted.
8209 JS::HandleValueArray elements =
8210 JS::HandleValueArray::fromMarkedLocation(values.Length(), values.Elements());
8212 transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements));
8213 if (transferArray.isNull()) {
8214 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
8215 return;
8216 }
8217 }
8219 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aError);
8220 }
8222 NS_IMETHODIMP
8223 nsGlobalWindow::PostMessageMoz(JS::Handle<JS::Value> aMessage,
8224 const nsAString& aOrigin,
8225 JS::Handle<JS::Value> aTransfer,
8226 JSContext* aCx)
8227 {
8228 ErrorResult rv;
8229 PostMessageMoz(aCx, aMessage, aOrigin, aTransfer, rv);
8231 return rv.ErrorCode();
8232 }
8234 class nsCloseEvent : public nsRunnable {
8236 nsRefPtr<nsGlobalWindow> mWindow;
8237 bool mIndirect;
8239 nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect)
8240 : mWindow(aWindow)
8241 , mIndirect(aIndirect)
8242 {}
8244 public:
8246 static nsresult
8247 PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) {
8248 nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
8249 nsresult rv = NS_DispatchToCurrentThread(ev);
8250 if (NS_SUCCEEDED(rv))
8251 aWindow->MaybeForgiveSpamCount();
8252 return rv;
8253 }
8255 NS_IMETHOD Run() {
8256 if (mWindow) {
8257 if (mIndirect) {
8258 return PostCloseEvent(mWindow, false);
8259 }
8260 mWindow->ReallyCloseWindow();
8261 }
8262 return NS_OK;
8263 }
8265 };
8267 bool
8268 nsGlobalWindow::CanClose()
8269 {
8270 if (!mDocShell)
8271 return true;
8273 // Ask the content viewer whether the toplevel window can close.
8274 // If the content viewer returns false, it is responsible for calling
8275 // Close() as soon as it is possible for the window to close.
8276 // This allows us to not close the window while printing is happening.
8278 nsCOMPtr<nsIContentViewer> cv;
8279 mDocShell->GetContentViewer(getter_AddRefs(cv));
8280 if (cv) {
8281 bool canClose;
8282 nsresult rv = cv->PermitUnload(false, &canClose);
8283 if (NS_SUCCEEDED(rv) && !canClose)
8284 return false;
8286 rv = cv->RequestWindowClose(&canClose);
8287 if (NS_SUCCEEDED(rv) && !canClose)
8288 return false;
8289 }
8291 return true;
8292 }
8294 void
8295 nsGlobalWindow::Close(ErrorResult& aError)
8296 {
8297 FORWARD_TO_OUTER_OR_THROW(Close, (aError), aError, );
8299 if (!mDocShell || IsInModalState() ||
8300 (IsFrame() && !mDocShell->GetIsBrowserOrApp())) {
8301 // window.close() is called on a frame in a frameset, on a window
8302 // that's already closed, or on a window for which there's
8303 // currently a modal dialog open. Ignore such calls.
8304 return;
8305 }
8307 if (mHavePendingClose) {
8308 // We're going to be closed anyway; do nothing since we don't want
8309 // to double-close
8310 return;
8311 }
8313 if (mBlockScriptedClosingFlag)
8314 {
8315 // A script's popup has been blocked and we don't want
8316 // the window to be closed directly after this event,
8317 // so the user can see that there was a blocked popup.
8318 return;
8319 }
8321 // Don't allow scripts from content to close non-app or non-neterror
8322 // windows that were not opened by script.
8323 nsAutoString url;
8324 mDoc->GetURL(url);
8325 if (!mDocShell->GetIsApp() &&
8326 !StringBeginsWith(url, NS_LITERAL_STRING("about:neterror")) &&
8327 !mHadOriginalOpener && !nsContentUtils::IsCallerChrome()) {
8328 bool allowClose = mAllowScriptsToClose ||
8329 Preferences::GetBool("dom.allow_scripts_to_close_windows", true);
8330 if (!allowClose) {
8331 // We're blocking the close operation
8332 // report localized error msg in JS console
8333 nsContentUtils::ReportToConsole(
8334 nsIScriptError::warningFlag,
8335 NS_LITERAL_CSTRING("DOM Window"), mDoc, // Better name for the category?
8336 nsContentUtils::eDOM_PROPERTIES,
8337 "WindowCloseBlockedWarning");
8339 return;
8340 }
8341 }
8343 if (!mInClose && !mIsClosed && !CanClose()) {
8344 return;
8345 }
8347 // Fire a DOM event notifying listeners that this window is about to
8348 // be closed. The tab UI code may choose to cancel the default
8349 // action for this event, if so, we won't actually close the window
8350 // (since the tab UI code will close the tab in stead). Sure, this
8351 // could be abused by content code, but do we care? I don't think
8352 // so...
8354 bool wasInClose = mInClose;
8355 mInClose = true;
8357 if (!DispatchCustomEvent("DOMWindowClose")) {
8358 // Someone chose to prevent the default action for this event, if
8359 // so, let's not close this window after all...
8361 mInClose = wasInClose;
8362 return;
8363 }
8365 aError = FinalClose();
8366 }
8368 NS_IMETHODIMP
8369 nsGlobalWindow::Close()
8370 {
8371 ErrorResult rv;
8372 Close(rv);
8374 return rv.ErrorCode();
8375 }
8377 nsresult
8378 nsGlobalWindow::ForceClose()
8379 {
8380 if (IsFrame() || !mDocShell) {
8381 // This may be a frame in a frameset, or a window that's already closed.
8382 // Ignore such calls.
8384 return NS_OK;
8385 }
8387 if (mHavePendingClose) {
8388 // We're going to be closed anyway; do nothing since we don't want
8389 // to double-close
8390 return NS_OK;
8391 }
8393 mInClose = true;
8395 DispatchCustomEvent("DOMWindowClose");
8397 return FinalClose();
8398 }
8400 nsresult
8401 nsGlobalWindow::FinalClose()
8402 {
8403 // Flag that we were closed.
8404 mIsClosed = true;
8406 // This stuff is non-sensical but incredibly fragile. The reasons for the
8407 // behavior here don't make sense today and may not have ever made sense,
8408 // but various bits of frontend code break when you change them. If you need
8409 // to fix up this behavior, feel free to. It's a righteous task, but involves
8410 // wrestling with various download manager tests, frontend code, and possible
8411 // broken addons. The chrome tests in toolkit/mozapps/downloads are a good
8412 // testing ground.
8413 //
8414 // In particular, if |win|'s JSContext is at the top of the stack, we must
8415 // complete _two_ round-trips to the event loop before the call to
8416 // ReallyCloseWindow. This allows setTimeout handlers that are set after
8417 // FinalClose() is called to run before the window is torn down.
8418 bool indirect = GetContextInternal() && // Occasionally null. See bug 877390.
8419 (nsContentUtils::GetCurrentJSContext() ==
8420 GetContextInternal()->GetNativeContext());
8421 if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) {
8422 ReallyCloseWindow();
8423 } else {
8424 mHavePendingClose = true;
8425 }
8427 return NS_OK;
8428 }
8431 void
8432 nsGlobalWindow::ReallyCloseWindow()
8433 {
8434 FORWARD_TO_OUTER_VOID(ReallyCloseWindow, ());
8436 // Make sure we never reenter this method.
8437 mHavePendingClose = true;
8439 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
8441 // If there's no treeOwnerAsWin, this window must already be closed.
8443 if (treeOwnerAsWin) {
8445 // but if we're a browser window we could be in some nasty
8446 // self-destroying cascade that we should mostly ignore
8448 if (mDocShell) {
8449 nsCOMPtr<nsIBrowserDOMWindow> bwin;
8450 nsCOMPtr<nsIDocShellTreeItem> rootItem;
8451 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
8452 nsCOMPtr<nsIDOMWindow> rootWin(do_GetInterface(rootItem));
8453 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
8454 if (chromeWin)
8455 chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
8457 if (rootWin) {
8458 /* Normally we destroy the entire window, but not if
8459 this DOM window belongs to a tabbed browser and doesn't
8460 correspond to a tab. This allows a well-behaved tab
8461 to destroy the container as it should but is a final measure
8462 to prevent an errant tab from doing so when it shouldn't.
8463 This works because we reach this code when we shouldn't only
8464 in the particular circumstance that we belong to a tab
8465 that has just been closed (and is therefore already missing
8466 from the list of browsers) (and has an unload handler
8467 that closes the window). */
8468 // XXXbz now that we have mHavePendingClose, is this needed?
8469 bool isTab = false;
8470 if (rootWin == this ||
8471 !bwin || (bwin->IsTabContentWindow(GetOuterWindowInternal(),
8472 &isTab), isTab))
8473 treeOwnerAsWin->Destroy();
8474 }
8475 }
8477 CleanUp();
8478 }
8479 }
8481 void
8482 nsGlobalWindow::EnterModalState()
8483 {
8484 MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
8486 // GetScriptableTop, not GetTop, so that EnterModalState works properly with
8487 // <iframe mozbrowser>.
8488 nsGlobalWindow* topWin = GetScriptableTop();
8490 if (!topWin) {
8491 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
8492 return;
8493 }
8495 // If there is an active ESM in this window, clear it. Otherwise, this can
8496 // cause a problem if a modal state is entered during a mouseup event.
8497 EventStateManager* activeESM =
8498 static_cast<EventStateManager*>(
8499 EventStateManager::GetActiveEventStateManager());
8500 if (activeESM && activeESM->GetPresContext()) {
8501 nsIPresShell* activeShell = activeESM->GetPresContext()->GetPresShell();
8502 if (activeShell && (
8503 nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(), mDoc) ||
8504 nsContentUtils::ContentIsCrossDocDescendantOf(mDoc, activeShell->GetDocument()))) {
8505 EventStateManager::ClearGlobalActiveContent(activeESM);
8507 activeShell->SetCapturingContent(nullptr, 0);
8509 if (activeShell) {
8510 nsRefPtr<nsFrameSelection> frameSelection = activeShell->FrameSelection();
8511 frameSelection->SetMouseDownState(false);
8512 }
8513 }
8514 }
8516 // If there are any drag and drop operations in flight, try to end them.
8517 nsCOMPtr<nsIDragService> ds =
8518 do_GetService("@mozilla.org/widget/dragservice;1");
8519 if (ds) {
8520 ds->EndDragSession(true);
8521 }
8523 // Clear the capturing content if it is under topDoc.
8524 // Usually the activeESM check above does that, but there are cases when
8525 // we don't have activeESM, or it is for different document.
8526 nsIDocument* topDoc = topWin->GetExtantDoc();
8527 nsIContent* capturingContent = nsIPresShell::GetCapturingContent();
8528 if (capturingContent && topDoc &&
8529 nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) {
8530 nsIPresShell::SetCapturingContent(nullptr, 0);
8531 }
8533 if (topWin->mModalStateDepth == 0) {
8534 NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
8536 mSuspendedDoc = topDoc;
8537 if (mSuspendedDoc) {
8538 mSuspendedDoc->SuppressEventHandling(nsIDocument::eAnimationsOnly);
8539 }
8540 }
8541 topWin->mModalStateDepth++;
8542 }
8544 // static
8545 void
8546 nsGlobalWindow::RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow,
8547 nsGlobalWindow *aWindow)
8548 {
8549 nsGlobalWindow *inner;
8551 // Return early if we're frozen or have no inner window.
8552 if (!(inner = aWindow->GetCurrentInnerWindowInternal()) ||
8553 inner->IsFrozen()) {
8554 return;
8555 }
8557 inner->RunTimeout(nullptr);
8559 // Check again if we're frozen since running pending timeouts
8560 // could've frozen us.
8561 if (inner->IsFrozen()) {
8562 return;
8563 }
8565 nsCOMPtr<nsIDOMWindowCollection> frames;
8566 aWindow->GetFrames(getter_AddRefs(frames));
8568 if (!frames) {
8569 return;
8570 }
8572 uint32_t i, length;
8573 if (NS_FAILED(frames->GetLength(&length)) || !length) {
8574 return;
8575 }
8577 for (i = 0; i < length && aTopWindow->mModalStateDepth == 0; i++) {
8578 nsCOMPtr<nsIDOMWindow> child;
8579 frames->Item(i, getter_AddRefs(child));
8581 if (!child) {
8582 return;
8583 }
8585 nsGlobalWindow *childWin =
8586 static_cast<nsGlobalWindow *>
8587 (static_cast<nsIDOMWindow *>
8588 (child.get()));
8590 RunPendingTimeoutsRecursive(aTopWindow, childWin);
8591 }
8592 }
8594 class nsPendingTimeoutRunner : public nsRunnable
8595 {
8596 public:
8597 nsPendingTimeoutRunner(nsGlobalWindow *aWindow)
8598 : mWindow(aWindow)
8599 {
8600 NS_ASSERTION(mWindow, "mWindow is null.");
8601 }
8603 NS_IMETHOD Run()
8604 {
8605 nsGlobalWindow::RunPendingTimeoutsRecursive(mWindow, mWindow);
8607 return NS_OK;
8608 }
8610 private:
8611 nsRefPtr<nsGlobalWindow> mWindow;
8612 };
8614 void
8615 nsGlobalWindow::LeaveModalState()
8616 {
8617 MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
8619 nsGlobalWindow* topWin = GetScriptableTop();
8621 if (!topWin) {
8622 NS_ERROR("Uh, LeaveModalState() called w/o a reachable top window?");
8623 return;
8624 }
8626 topWin->mModalStateDepth--;
8628 if (topWin->mModalStateDepth == 0) {
8629 nsCOMPtr<nsIRunnable> runner = new nsPendingTimeoutRunner(topWin);
8630 if (NS_FAILED(NS_DispatchToCurrentThread(runner)))
8631 NS_WARNING("failed to dispatch pending timeout runnable");
8633 if (mSuspendedDoc) {
8634 nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc();
8635 mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eAnimationsOnly,
8636 currentDoc == mSuspendedDoc);
8637 mSuspendedDoc = nullptr;
8638 }
8639 }
8641 // Remember the time of the last dialog quit.
8642 nsGlobalWindow *inner = topWin->GetCurrentInnerWindowInternal();
8643 if (inner)
8644 inner->mLastDialogQuitTime = TimeStamp::Now();
8645 }
8647 bool
8648 nsGlobalWindow::IsInModalState()
8649 {
8650 nsGlobalWindow *topWin = GetScriptableTop();
8652 if (!topWin) {
8653 NS_ERROR("Uh, IsInModalState() called w/o a reachable top window?");
8655 return false;
8656 }
8658 return topWin->mModalStateDepth != 0;
8659 }
8661 // static
8662 void
8663 nsGlobalWindow::NotifyDOMWindowDestroyed(nsGlobalWindow* aWindow) {
8664 nsCOMPtr<nsIObserverService> observerService =
8665 services::GetObserverService();
8666 if (observerService) {
8667 observerService->
8668 NotifyObservers(ToSupports(aWindow),
8669 DOM_WINDOW_DESTROYED_TOPIC, nullptr);
8670 }
8671 }
8673 class WindowDestroyedEvent : public nsRunnable
8674 {
8675 public:
8676 WindowDestroyedEvent(nsPIDOMWindow* aWindow, uint64_t aID,
8677 const char* aTopic) :
8678 mID(aID), mTopic(aTopic)
8679 {
8680 mWindow = do_GetWeakReference(aWindow);
8681 }
8683 NS_IMETHOD Run()
8684 {
8685 nsCOMPtr<nsIObserverService> observerService =
8686 do_GetService("@mozilla.org/observer-service;1");
8687 if (observerService) {
8688 nsCOMPtr<nsISupportsPRUint64> wrapper =
8689 do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
8690 if (wrapper) {
8691 wrapper->SetData(mID);
8692 observerService->NotifyObservers(wrapper, mTopic.get(), nullptr);
8693 }
8694 }
8696 bool skipNukeCrossCompartment = false;
8697 #ifndef DEBUG
8698 nsCOMPtr<nsIAppStartup> appStartup =
8699 do_GetService(NS_APPSTARTUP_CONTRACTID);
8701 if (appStartup) {
8702 appStartup->GetShuttingDown(&skipNukeCrossCompartment);
8703 }
8704 #endif
8706 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
8707 if (!skipNukeCrossCompartment && window) {
8708 nsGlobalWindow* currentInner =
8709 window->IsInnerWindow() ? static_cast<nsGlobalWindow*>(window.get()) :
8710 static_cast<nsGlobalWindow*>(window->GetCurrentInnerWindow());
8711 NS_ENSURE_TRUE(currentInner, NS_OK);
8713 AutoSafeJSContext cx;
8714 JS::Rooted<JSObject*> obj(cx, currentInner->FastGetGlobalJSObject());
8715 // We only want to nuke wrappers for the chrome->content case
8716 if (obj && !js::IsSystemCompartment(js::GetObjectCompartment(obj))) {
8717 js::NukeCrossCompartmentWrappers(cx,
8718 js::ChromeCompartmentsOnly(),
8719 js::SingleCompartment(js::GetObjectCompartment(obj)),
8720 window->IsInnerWindow() ? js::DontNukeWindowReferences :
8721 js::NukeWindowReferences);
8722 }
8723 }
8725 return NS_OK;
8726 }
8728 private:
8729 uint64_t mID;
8730 nsCString mTopic;
8731 nsWeakPtr mWindow;
8732 };
8734 void
8735 nsGlobalWindow::NotifyWindowIDDestroyed(const char* aTopic)
8736 {
8737 nsRefPtr<nsIRunnable> runnable = new WindowDestroyedEvent(this, mWindowID, aTopic);
8738 nsresult rv = NS_DispatchToCurrentThread(runnable);
8739 if (NS_SUCCEEDED(rv)) {
8740 mNotifiedIDDestroyed = true;
8741 }
8742 }
8744 // static
8745 void
8746 nsGlobalWindow::NotifyDOMWindowFrozen(nsGlobalWindow* aWindow) {
8747 if (aWindow && aWindow->IsInnerWindow()) {
8748 nsCOMPtr<nsIObserverService> observerService =
8749 services::GetObserverService();
8750 if (observerService) {
8751 observerService->
8752 NotifyObservers(ToSupports(aWindow),
8753 DOM_WINDOW_FROZEN_TOPIC, nullptr);
8754 }
8755 }
8756 }
8758 // static
8759 void
8760 nsGlobalWindow::NotifyDOMWindowThawed(nsGlobalWindow* aWindow) {
8761 if (aWindow && aWindow->IsInnerWindow()) {
8762 nsCOMPtr<nsIObserverService> observerService =
8763 services::GetObserverService();
8764 if (observerService) {
8765 observerService->
8766 NotifyObservers(ToSupports(aWindow),
8767 DOM_WINDOW_THAWED_TOPIC, nullptr);
8768 }
8769 }
8770 }
8772 JSObject*
8773 nsGlobalWindow::GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey)
8774 {
8775 AutoSafeJSContext cx;
8776 JS::Rooted<JSObject*> handler(cx);
8777 if (mCachedXBLPrototypeHandlers) {
8778 mCachedXBLPrototypeHandlers->Get(aKey, handler.address());
8779 }
8780 return handler;
8781 }
8783 void
8784 nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
8785 JS::Handle<JSObject*> aHandler)
8786 {
8787 if (!mCachedXBLPrototypeHandlers) {
8788 mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
8789 PreserveWrapper(ToSupports(this));
8790 }
8792 mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
8793 }
8795 /**
8796 * GetScriptableFrameElement is called when script reads
8797 * nsIGlobalWindow::frameElement.
8798 *
8799 * In contrast to GetRealFrameElement, GetScriptableFrameElement says that the
8800 * window contained by an <iframe mozbrowser> or <iframe mozapp> has no frame
8801 * element (effectively treating a mozbrowser the same as a content/chrome
8802 * boundary).
8803 */
8804 NS_IMETHODIMP
8805 nsGlobalWindow::GetScriptableFrameElement(nsIDOMElement** aFrameElement)
8806 {
8807 ErrorResult rv;
8808 nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(GetFrameElement(rv));
8809 if (rv.Failed()) {
8810 return rv.ErrorCode();
8811 }
8813 frameElement.forget(aFrameElement);
8815 return NS_OK;
8816 }
8818 Element*
8819 nsGlobalWindow::GetFrameElement(ErrorResult& aError)
8820 {
8821 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aError), aError, nullptr);
8823 if (!mDocShell || mDocShell->GetIsBrowserOrApp()) {
8824 return nullptr;
8825 }
8827 // Per HTML5, the frameElement getter returns null in cross-origin situations.
8828 Element* element = GetRealFrameElement(aError);
8829 if (aError.Failed() || !element) {
8830 return nullptr;
8831 }
8832 if (!nsContentUtils::GetSubjectPrincipal()->
8833 SubsumesConsideringDomain(element->NodePrincipal())) {
8834 return nullptr;
8835 }
8836 return element;
8837 }
8839 Element*
8840 nsGlobalWindow::GetRealFrameElement(ErrorResult& aError)
8841 {
8842 FORWARD_TO_OUTER_OR_THROW(GetRealFrameElement, (aError), aError, nullptr);
8844 if (!mDocShell) {
8845 return nullptr;
8846 }
8848 nsCOMPtr<nsIDocShell> parent;
8849 mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
8851 if (!parent || parent == mDocShell) {
8852 // We're at a chrome boundary, don't expose the chrome iframe
8853 // element to content code.
8854 return nullptr;
8855 }
8857 return mFrameElement;
8858 }
8860 /**
8861 * nsIGlobalWindow::GetFrameElement (when called from C++) is just a wrapper
8862 * around GetRealFrameElement.
8863 */
8864 NS_IMETHODIMP
8865 nsGlobalWindow::GetRealFrameElement(nsIDOMElement** aFrameElement)
8866 {
8867 ErrorResult rv;
8868 nsCOMPtr<nsIDOMElement> frameElement =
8869 do_QueryInterface(GetRealFrameElement(rv));
8870 frameElement.forget(aFrameElement);
8872 return rv.ErrorCode();
8873 }
8875 // Helper for converting window.showModalDialog() options (list of ';'
8876 // separated name (:|=) value pairs) to a format that's parsable by
8877 // our normal window opening code.
8879 void
8880 ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult)
8881 {
8882 nsAString::const_iterator end;
8883 aOptions.EndReading(end);
8885 nsAString::const_iterator iter;
8886 aOptions.BeginReading(iter);
8888 while (iter != end) {
8889 // Skip whitespace.
8890 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8891 ++iter;
8892 }
8894 nsAString::const_iterator name_start = iter;
8896 // Skip characters until we find whitespace, ';', ':', or '='
8897 while (iter != end && !nsCRT::IsAsciiSpace(*iter) &&
8898 *iter != ';' &&
8899 *iter != ':' &&
8900 *iter != '=') {
8901 ++iter;
8902 }
8904 nsAString::const_iterator name_end = iter;
8906 // Skip whitespace.
8907 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8908 ++iter;
8909 }
8911 if (*iter == ';') {
8912 // No value found, skip the ';' and keep going.
8913 ++iter;
8915 continue;
8916 }
8918 nsAString::const_iterator value_start = iter;
8919 nsAString::const_iterator value_end = iter;
8921 if (*iter == ':' || *iter == '=') {
8922 // We found name followed by ':' or '='. Look for a value.
8924 iter++; // Skip the ':' or '='
8926 // Skip whitespace.
8927 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8928 ++iter;
8929 }
8931 value_start = iter;
8933 // Skip until we find whitespace, or ';'.
8934 while (iter != end && !nsCRT::IsAsciiSpace(*iter) &&
8935 *iter != ';') {
8936 ++iter;
8937 }
8939 value_end = iter;
8941 // Skip whitespace.
8942 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8943 ++iter;
8944 }
8945 }
8947 const nsDependentSubstring& name = Substring(name_start, name_end);
8948 const nsDependentSubstring& value = Substring(value_start, value_end);
8950 if (name.LowerCaseEqualsLiteral("center")) {
8951 if (value.LowerCaseEqualsLiteral("on") ||
8952 value.LowerCaseEqualsLiteral("yes") ||
8953 value.LowerCaseEqualsLiteral("1")) {
8954 aResult.AppendLiteral(",centerscreen=1");
8955 }
8956 } else if (name.LowerCaseEqualsLiteral("dialogwidth")) {
8957 if (!value.IsEmpty()) {
8958 aResult.AppendLiteral(",width=");
8959 aResult.Append(value);
8960 }
8961 } else if (name.LowerCaseEqualsLiteral("dialogheight")) {
8962 if (!value.IsEmpty()) {
8963 aResult.AppendLiteral(",height=");
8964 aResult.Append(value);
8965 }
8966 } else if (name.LowerCaseEqualsLiteral("dialogtop")) {
8967 if (!value.IsEmpty()) {
8968 aResult.AppendLiteral(",top=");
8969 aResult.Append(value);
8970 }
8971 } else if (name.LowerCaseEqualsLiteral("dialogleft")) {
8972 if (!value.IsEmpty()) {
8973 aResult.AppendLiteral(",left=");
8974 aResult.Append(value);
8975 }
8976 } else if (name.LowerCaseEqualsLiteral("resizable")) {
8977 if (value.LowerCaseEqualsLiteral("on") ||
8978 value.LowerCaseEqualsLiteral("yes") ||
8979 value.LowerCaseEqualsLiteral("1")) {
8980 aResult.AppendLiteral(",resizable=1");
8981 }
8982 } else if (name.LowerCaseEqualsLiteral("scroll")) {
8983 if (value.LowerCaseEqualsLiteral("off") ||
8984 value.LowerCaseEqualsLiteral("no") ||
8985 value.LowerCaseEqualsLiteral("0")) {
8986 aResult.AppendLiteral(",scrollbars=0");
8987 }
8988 }
8990 if (iter == end) {
8991 break;
8992 }
8994 iter++;
8995 }
8996 }
8998 already_AddRefed<nsIVariant>
8999 nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
9000 const nsAString& aOptions, ErrorResult& aError)
9001 {
9002 if (mDoc) {
9003 mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog);
9004 }
9006 FORWARD_TO_OUTER_OR_THROW(ShowModalDialog,
9007 (aUrl, aArgument, aOptions, aError), aError,
9008 nullptr);
9010 if (Preferences::GetBool("dom.disable_window_showModalDialog", false)) {
9011 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9012 return nullptr;
9013 }
9015 nsRefPtr<DialogValueHolder> argHolder =
9016 new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), aArgument);
9018 // Before bringing up the window/dialog, unsuppress painting and flush
9019 // pending reflows.
9020 EnsureReflowFlushAndPaint();
9022 if (!AreDialogsEnabled()) {
9023 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9024 return nullptr;
9025 }
9027 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
9028 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9029 return nullptr;
9030 }
9032 nsCOMPtr<nsIDOMWindow> dlgWin;
9033 nsAutoString options(NS_LITERAL_STRING("-moz-internal-modal=1,status=1"));
9035 ConvertDialogOptions(aOptions, options);
9037 options.AppendLiteral(",scrollbars=1,centerscreen=1,resizable=0");
9039 EnterModalState();
9040 uint32_t oldMicroTaskLevel = nsContentUtils::MicroTaskLevel();
9041 nsContentUtils::SetMicroTaskLevel(0);
9042 aError = OpenInternal(aUrl, EmptyString(), options,
9043 false, // aDialog
9044 true, // aContentModal
9045 true, // aCalledNoScript
9046 true, // aDoJSFixups
9047 true, // aNavigate
9048 nullptr, argHolder, // args
9049 GetPrincipal(), // aCalleePrincipal
9050 nullptr, // aJSCallerContext
9051 getter_AddRefs(dlgWin));
9052 nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel);
9053 LeaveModalState();
9054 if (aError.Failed()) {
9055 return nullptr;
9056 }
9058 nsCOMPtr<nsIDOMModalContentWindow> dialog = do_QueryInterface(dlgWin);
9059 if (!dialog) {
9060 return nullptr;
9061 }
9063 nsCOMPtr<nsIVariant> retVal;
9064 aError = dialog->GetReturnValue(getter_AddRefs(retVal));
9065 MOZ_ASSERT(!aError.Failed());
9067 return retVal.forget();
9068 }
9070 void
9071 nsGlobalWindow::ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
9072 JS::Handle<JS::Value> aArgument,
9073 const nsAString& aOptions,
9074 JS::MutableHandle<JS::Value> aRetval,
9075 ErrorResult& aError)
9076 {
9077 nsCOMPtr<nsIVariant> args;
9078 aError = nsContentUtils::XPConnect()->JSToVariant(aCx,
9079 aArgument,
9080 getter_AddRefs(args));
9082 nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aUrl, args, aOptions, aError);
9083 if (aError.Failed()) {
9084 return;
9085 }
9087 JS::Rooted<JS::Value> result(aCx);
9088 if (retVal) {
9089 aError = nsContentUtils::XPConnect()->VariantToJS(aCx,
9090 FastGetGlobalJSObject(),
9091 retVal, aRetval);
9092 } else {
9093 aRetval.setNull();
9094 }
9095 }
9097 NS_IMETHODIMP
9098 nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs_,
9099 const nsAString& aOptions, uint8_t aArgc,
9100 nsIVariant **aRetVal)
9101 {
9102 // Per-spec the |arguments| parameter is supposed to pass through unmodified.
9103 // However, XPConnect default-initializes variants to null, rather than
9104 // undefined. Fix this up here.
9105 nsCOMPtr<nsIVariant> aArgs = aArgs_;
9106 if (aArgc < 1) {
9107 aArgs = CreateVoidVariant();
9108 }
9110 ErrorResult rv;
9111 nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aURI, aArgs, aOptions, rv);
9112 retVal.forget(aRetVal);
9114 return rv.ErrorCode();
9115 }
9117 class CommandDispatcher : public nsRunnable
9118 {
9119 public:
9120 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
9121 const nsAString& aAction)
9122 : mDispatcher(aDispatcher), mAction(aAction) {}
9124 NS_IMETHOD Run()
9125 {
9126 return mDispatcher->UpdateCommands(mAction);
9127 }
9129 nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
9130 nsString mAction;
9131 };
9133 NS_IMETHODIMP
9134 nsGlobalWindow::UpdateCommands(const nsAString& anAction)
9135 {
9136 nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
9137 if (!rootWindow)
9138 return NS_OK;
9140 nsCOMPtr<nsIDOMXULDocument> xulDoc =
9141 do_QueryInterface(rootWindow->GetExtantDoc());
9142 // See if we contain a XUL document.
9143 if (xulDoc) {
9144 // Retrieve the command dispatcher and call updateCommands on it.
9145 nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher;
9146 xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher));
9147 if (xulCommandDispatcher) {
9148 nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher,
9149 anAction));
9150 }
9151 }
9153 return NS_OK;
9154 }
9156 Selection*
9157 nsGlobalWindow::GetSelection(ErrorResult& aError)
9158 {
9159 FORWARD_TO_OUTER_OR_THROW(GetSelection, (aError), aError, nullptr);
9161 if (!mDocShell) {
9162 return nullptr;
9163 }
9165 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
9166 if (!presShell) {
9167 return nullptr;
9168 }
9170 return static_cast<Selection*>(presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL));
9171 }
9173 NS_IMETHODIMP
9174 nsGlobalWindow::GetSelection(nsISelection** aSelection)
9175 {
9176 ErrorResult rv;
9177 nsCOMPtr<nsISelection> selection = GetSelection(rv);
9178 selection.forget(aSelection);
9180 return rv.ErrorCode();
9181 }
9183 bool
9184 nsGlobalWindow::Find(const nsAString& aString, bool aCaseSensitive,
9185 bool aBackwards, bool aWrapAround, bool aWholeWord,
9186 bool aSearchInFrames, bool aShowDialog,
9187 ErrorResult& aError)
9188 {
9189 if (Preferences::GetBool("dom.disable_window_find", false)) {
9190 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9191 return false;
9192 }
9194 FORWARD_TO_OUTER_OR_THROW(Find,
9195 (aString, aCaseSensitive, aBackwards, aWrapAround,
9196 aWholeWord, aSearchInFrames, aShowDialog, aError),
9197 aError, false);
9199 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
9200 if (!finder) {
9201 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9202 return false;
9203 }
9205 // Set the options of the search
9206 aError = finder->SetSearchString(PromiseFlatString(aString).get());
9207 if (aError.Failed()) {
9208 return false;
9209 }
9210 finder->SetMatchCase(aCaseSensitive);
9211 finder->SetFindBackwards(aBackwards);
9212 finder->SetWrapFind(aWrapAround);
9213 finder->SetEntireWord(aWholeWord);
9214 finder->SetSearchFrames(aSearchInFrames);
9216 // the nsIWebBrowserFind is initialized to use this window
9217 // as the search root, but uses focus to set the current search
9218 // frame. If we're being called from JS (as here), this window
9219 // should be the current search frame.
9220 nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
9221 if (framesFinder) {
9222 framesFinder->SetRootSearchFrame(this); // paranoia
9223 framesFinder->SetCurrentSearchFrame(this);
9224 }
9226 // The Find API does not accept empty strings. Launch the Find Dialog.
9227 if (aString.IsEmpty() || aShowDialog) {
9228 // See if the find dialog is already up using nsIWindowMediator
9229 nsCOMPtr<nsIWindowMediator> windowMediator =
9230 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
9232 nsCOMPtr<nsIDOMWindow> findDialog;
9234 if (windowMediator) {
9235 windowMediator->GetMostRecentWindow(MOZ_UTF16("findInPage"),
9236 getter_AddRefs(findDialog));
9237 }
9239 if (findDialog) {
9240 // The Find dialog is already open, bring it to the top.
9241 aError = findDialog->Focus();
9242 } else if (finder) {
9243 // Open a Find dialog
9244 nsCOMPtr<nsIDOMWindow> dialog;
9245 aError = OpenDialog(NS_LITERAL_STRING("chrome://global/content/finddialog.xul"),
9246 NS_LITERAL_STRING("_blank"),
9247 NS_LITERAL_STRING("chrome, resizable=no, dependent=yes"),
9248 finder, getter_AddRefs(dialog));
9249 }
9251 return false;
9252 }
9254 // Launch the search with the passed in search string
9255 bool didFind = false;
9256 aError = finder->FindNext(&didFind);
9257 return didFind;
9258 }
9260 NS_IMETHODIMP
9261 nsGlobalWindow::Find(const nsAString& aStr, bool aCaseSensitive,
9262 bool aBackwards, bool aWrapAround, bool aWholeWord,
9263 bool aSearchInFrames, bool aShowDialog,
9264 bool *aDidFind)
9265 {
9266 ErrorResult rv;
9267 *aDidFind = Find(aStr, aCaseSensitive, aBackwards, aWrapAround, aWholeWord,
9268 aSearchInFrames, aShowDialog, rv);
9270 return rv.ErrorCode();
9271 }
9273 void
9274 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
9275 nsAString& aBinaryData, ErrorResult& aError)
9276 {
9277 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
9278 }
9280 NS_IMETHODIMP
9281 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
9282 nsAString& aBinaryData)
9283 {
9284 ErrorResult rv;
9285 Atob(aAsciiBase64String, aBinaryData, rv);
9287 return rv.ErrorCode();
9288 }
9290 void
9291 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
9292 nsAString& aAsciiBase64String, ErrorResult& aError)
9293 {
9294 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
9295 }
9297 NS_IMETHODIMP
9298 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
9299 nsAString& aAsciiBase64String)
9300 {
9301 ErrorResult rv;
9302 Btoa(aBinaryData, aAsciiBase64String, rv);
9304 return rv.ErrorCode();
9305 }
9307 //*****************************************************************************
9308 // nsGlobalWindow::nsIDOMEventTarget
9309 //*****************************************************************************
9311 NS_IMETHODIMP
9312 nsGlobalWindow::RemoveEventListener(const nsAString& aType,
9313 nsIDOMEventListener* aListener,
9314 bool aUseCapture)
9315 {
9316 if (nsRefPtr<EventListenerManager> elm = GetExistingListenerManager()) {
9317 elm->RemoveEventListener(aType, aListener, aUseCapture);
9318 }
9319 return NS_OK;
9320 }
9322 NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsGlobalWindow)
9324 NS_IMETHODIMP
9325 nsGlobalWindow::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal)
9326 {
9327 FORWARD_TO_INNER(DispatchEvent, (aEvent, aRetVal), NS_OK);
9329 if (!IsCurrentInnerWindow()) {
9330 NS_WARNING("DispatchEvent called on non-current inner window, dropping. "
9331 "Please check the window in the caller instead.");
9332 return NS_ERROR_FAILURE;
9333 }
9335 if (!mDoc) {
9336 return NS_ERROR_FAILURE;
9337 }
9339 // Obtain a presentation shell
9340 nsIPresShell *shell = mDoc->GetShell();
9341 nsRefPtr<nsPresContext> presContext;
9342 if (shell) {
9343 // Retrieve the context
9344 presContext = shell->GetPresContext();
9345 }
9347 nsEventStatus status = nsEventStatus_eIgnore;
9348 nsresult rv =
9349 EventDispatcher::DispatchDOMEvent(GetOuterWindow(), nullptr, aEvent,
9350 presContext, &status);
9352 *aRetVal = (status != nsEventStatus_eConsumeNoDefault);
9353 return rv;
9354 }
9356 NS_IMETHODIMP
9357 nsGlobalWindow::AddEventListener(const nsAString& aType,
9358 nsIDOMEventListener *aListener,
9359 bool aUseCapture, bool aWantsUntrusted,
9360 uint8_t aOptionalArgc)
9361 {
9362 NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
9363 "Won't check if this is chrome, you want to set "
9364 "aWantsUntrusted to false or make the aWantsUntrusted "
9365 "explicit by making optional_argc non-zero.");
9367 if (IsOuterWindow() && mInnerWindow &&
9368 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9369 return NS_ERROR_DOM_SECURITY_ERR;
9370 }
9372 if (!aWantsUntrusted &&
9373 (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
9374 aWantsUntrusted = true;
9375 }
9377 EventListenerManager* manager = GetOrCreateListenerManager();
9378 NS_ENSURE_STATE(manager);
9379 manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted);
9380 return NS_OK;
9381 }
9383 void
9384 nsGlobalWindow::AddEventListener(const nsAString& aType,
9385 EventListener* aListener,
9386 bool aUseCapture,
9387 const Nullable<bool>& aWantsUntrusted,
9388 ErrorResult& aRv)
9389 {
9390 if (IsOuterWindow() && mInnerWindow &&
9391 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9392 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
9393 return;
9394 }
9396 bool wantsUntrusted;
9397 if (aWantsUntrusted.IsNull()) {
9398 wantsUntrusted = !nsContentUtils::IsChromeDoc(mDoc);
9399 } else {
9400 wantsUntrusted = aWantsUntrusted.Value();
9401 }
9403 EventListenerManager* manager = GetOrCreateListenerManager();
9404 if (!manager) {
9405 aRv.Throw(NS_ERROR_UNEXPECTED);
9406 return;
9407 }
9408 manager->AddEventListener(aType, aListener, aUseCapture, wantsUntrusted);
9409 }
9411 NS_IMETHODIMP
9412 nsGlobalWindow::AddSystemEventListener(const nsAString& aType,
9413 nsIDOMEventListener *aListener,
9414 bool aUseCapture,
9415 bool aWantsUntrusted,
9416 uint8_t aOptionalArgc)
9417 {
9418 NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
9419 "Won't check if this is chrome, you want to set "
9420 "aWantsUntrusted to false or make the aWantsUntrusted "
9421 "explicit by making optional_argc non-zero.");
9423 if (IsOuterWindow() && mInnerWindow &&
9424 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9425 return NS_ERROR_DOM_SECURITY_ERR;
9426 }
9428 if (!aWantsUntrusted &&
9429 (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
9430 aWantsUntrusted = true;
9431 }
9433 return NS_AddSystemEventListener(this, aType, aListener, aUseCapture,
9434 aWantsUntrusted);
9435 }
9437 EventListenerManager*
9438 nsGlobalWindow::GetOrCreateListenerManager()
9439 {
9440 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
9442 if (!mListenerManager) {
9443 mListenerManager =
9444 new EventListenerManager(static_cast<EventTarget*>(this));
9445 }
9447 return mListenerManager;
9448 }
9450 EventListenerManager*
9451 nsGlobalWindow::GetExistingListenerManager() const
9452 {
9453 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
9455 return mListenerManager;
9456 }
9458 nsIScriptContext*
9459 nsGlobalWindow::GetContextForEventHandlers(nsresult* aRv)
9460 {
9461 *aRv = NS_ERROR_UNEXPECTED;
9462 NS_ENSURE_TRUE(!IsInnerWindow() || IsCurrentInnerWindow(), nullptr);
9464 nsIScriptContext* scx;
9465 if ((scx = GetContext())) {
9466 *aRv = NS_OK;
9467 return scx;
9468 }
9469 return nullptr;
9470 }
9472 //*****************************************************************************
9473 // nsGlobalWindow::nsPIDOMWindow
9474 //*****************************************************************************
9476 nsPIDOMWindow*
9477 nsGlobalWindow::GetPrivateParent()
9478 {
9479 MOZ_ASSERT(IsOuterWindow());
9481 nsCOMPtr<nsIDOMWindow> parent;
9482 GetParent(getter_AddRefs(parent));
9484 if (static_cast<nsIDOMWindow *>(this) == parent.get()) {
9485 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
9486 if (!chromeElement)
9487 return nullptr; // This is ok, just means a null parent.
9489 nsIDocument* doc = chromeElement->GetDocument();
9490 if (!doc)
9491 return nullptr; // This is ok, just means a null parent.
9493 return doc->GetWindow();
9494 }
9496 if (parent) {
9497 return static_cast<nsGlobalWindow *>
9498 (static_cast<nsIDOMWindow*>(parent.get()));
9499 }
9501 return nullptr;
9502 }
9504 nsPIDOMWindow*
9505 nsGlobalWindow::GetPrivateRoot()
9506 {
9507 if (IsInnerWindow()) {
9508 nsGlobalWindow* outer = GetOuterWindowInternal();
9509 if (!outer) {
9510 NS_WARNING("No outer window available!");
9511 return nullptr;
9512 }
9513 return outer->GetPrivateRoot();
9514 }
9516 nsCOMPtr<nsIDOMWindow> top;
9517 GetTop(getter_AddRefs(top));
9519 nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler));
9520 if (chromeElement) {
9521 nsIDocument* doc = chromeElement->GetDocument();
9522 if (doc) {
9523 nsIDOMWindow *parent = doc->GetWindow();
9524 if (parent) {
9525 parent->GetTop(getter_AddRefs(top));
9526 }
9527 }
9528 }
9530 return static_cast<nsGlobalWindow*>(top.get());
9531 }
9534 nsIDOMLocation*
9535 nsGlobalWindow::GetLocation(ErrorResult& aError)
9536 {
9537 FORWARD_TO_INNER_OR_THROW(GetLocation, (aError), aError, nullptr);
9539 nsIDocShell *docShell = GetDocShell();
9540 if (!mLocation && docShell) {
9541 mLocation = new nsLocation(docShell);
9542 }
9543 return mLocation;
9544 }
9546 NS_IMETHODIMP
9547 nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation)
9548 {
9549 ErrorResult rv;
9550 nsCOMPtr<nsIDOMLocation> location = GetLocation(rv);
9551 location.forget(aLocation);
9553 return rv.ErrorCode();
9554 }
9556 void
9557 nsGlobalWindow::ActivateOrDeactivate(bool aActivate)
9558 {
9559 MOZ_ASSERT(IsOuterWindow());
9561 // Set / unset mIsActive on the top level window, which is used for the
9562 // :-moz-window-inactive pseudoclass, and its sheet (if any).
9563 nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
9564 if (!mainWidget)
9565 return;
9567 // Get the top level widget (if the main widget is a sheet, this will
9568 // be the sheet's top (non-sheet) parent).
9569 nsCOMPtr<nsIWidget> topLevelWidget = mainWidget->GetSheetWindowParent();
9570 if (!topLevelWidget) {
9571 topLevelWidget = mainWidget;
9572 }
9574 nsCOMPtr<nsPIDOMWindow> piMainWindow(
9575 do_QueryInterface(static_cast<nsIDOMWindow*>(this)));
9576 piMainWindow->SetActive(aActivate);
9578 if (mainWidget != topLevelWidget) {
9579 // This is a workaround for the following problem:
9580 // When a window with an open sheet gains or loses focus, only the sheet
9581 // window receives the NS_ACTIVATE/NS_DEACTIVATE event. However the
9582 // styling of the containing top level window also needs to change. We
9583 // get around this by calling nsPIDOMWindow::SetActive() on both windows.
9585 // Get the top level widget's nsGlobalWindow
9586 nsCOMPtr<nsIDOMWindow> topLevelWindow;
9588 // widgetListener should be a nsXULWindow
9589 nsIWidgetListener* listener = topLevelWidget->GetWidgetListener();
9590 if (listener) {
9591 nsCOMPtr<nsIXULWindow> window = listener->GetXULWindow();
9592 nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(window));
9593 topLevelWindow = do_GetInterface(req);
9594 }
9596 if (topLevelWindow) {
9597 nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(topLevelWindow));
9598 piWin->SetActive(aActivate);
9599 }
9600 }
9601 }
9603 static bool
9604 NotifyDocumentTree(nsIDocument* aDocument, void* aData)
9605 {
9606 aDocument->EnumerateSubDocuments(NotifyDocumentTree, nullptr);
9607 aDocument->DocumentStatesChanged(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
9608 return true;
9609 }
9611 void
9612 nsGlobalWindow::SetActive(bool aActive)
9613 {
9614 nsPIDOMWindow::SetActive(aActive);
9615 NotifyDocumentTree(mDoc, nullptr);
9616 }
9618 void nsGlobalWindow::SetIsBackground(bool aIsBackground)
9619 {
9620 MOZ_ASSERT(IsOuterWindow());
9622 bool resetTimers = (!aIsBackground && IsBackground());
9623 nsPIDOMWindow::SetIsBackground(aIsBackground);
9624 if (resetTimers) {
9625 ResetTimersForNonBackgroundWindow();
9626 }
9627 #ifdef MOZ_GAMEPAD
9628 if (!aIsBackground) {
9629 nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
9630 if (inner) {
9631 inner->SyncGamepadState();
9632 }
9633 }
9634 #endif
9635 }
9637 void nsGlobalWindow::MaybeUpdateTouchState()
9638 {
9639 FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
9641 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9643 nsCOMPtr<nsIDOMWindow> focusedWindow;
9644 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
9646 if(this == focusedWindow) {
9647 UpdateTouchState();
9648 }
9650 if (mMayHaveTouchEventListener) {
9651 nsCOMPtr<nsIObserverService> observerService =
9652 services::GetObserverService();
9654 if (observerService) {
9655 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
9656 DOM_TOUCH_LISTENER_ADDED,
9657 nullptr);
9658 }
9659 }
9660 }
9662 void nsGlobalWindow::UpdateTouchState()
9663 {
9664 FORWARD_TO_INNER_VOID(UpdateTouchState, ());
9666 nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
9667 if (!mainWidget) {
9668 return;
9669 }
9671 if (mMayHaveTouchEventListener) {
9672 mainWidget->RegisterTouchWindow();
9673 } else {
9674 mainWidget->UnregisterTouchWindow();
9675 }
9676 }
9678 void
9679 nsGlobalWindow::EnableGamepadUpdates()
9680 {
9681 FORWARD_TO_INNER_VOID(EnableGamepadUpdates, ());
9682 if (mHasGamepad) {
9683 #ifdef MOZ_GAMEPAD
9684 nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
9685 if (gamepadsvc) {
9686 gamepadsvc->AddListener(this);
9687 }
9688 #endif
9689 }
9690 }
9692 void
9693 nsGlobalWindow::DisableGamepadUpdates()
9694 {
9695 FORWARD_TO_INNER_VOID(DisableGamepadUpdates, ());
9696 if (mHasGamepad) {
9697 #ifdef MOZ_GAMEPAD
9698 nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
9699 if (gamepadsvc) {
9700 gamepadsvc->RemoveListener(this);
9701 }
9702 #endif
9703 }
9704 }
9706 void
9707 nsGlobalWindow::SetChromeEventHandler(EventTarget* aChromeEventHandler)
9708 {
9709 MOZ_ASSERT(IsOuterWindow());
9711 SetChromeEventHandlerInternal(aChromeEventHandler);
9712 // update the chrome event handler on all our inner windows
9713 for (nsGlobalWindow *inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
9714 inner != this;
9715 inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
9716 NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this,
9717 "bad outer window pointer");
9718 inner->SetChromeEventHandlerInternal(aChromeEventHandler);
9719 }
9720 }
9722 static bool IsLink(nsIContent* aContent)
9723 {
9724 return aContent && (aContent->IsHTML(nsGkAtoms::a) ||
9725 aContent->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
9726 nsGkAtoms::simple, eCaseMatters));
9727 }
9729 void
9730 nsGlobalWindow::SetFocusedNode(nsIContent* aNode,
9731 uint32_t aFocusMethod,
9732 bool aNeedsFocus)
9733 {
9734 FORWARD_TO_INNER_VOID(SetFocusedNode, (aNode, aFocusMethod, aNeedsFocus));
9736 if (aNode && aNode->GetCurrentDoc() != mDoc) {
9737 NS_WARNING("Trying to set focus to a node from a wrong document");
9738 return;
9739 }
9741 if (mCleanedUp) {
9742 NS_ASSERTION(!aNode, "Trying to focus cleaned up window!");
9743 aNode = nullptr;
9744 aNeedsFocus = false;
9745 }
9746 if (mFocusedNode != aNode) {
9747 UpdateCanvasFocus(false, aNode);
9748 mFocusedNode = aNode;
9749 mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
9750 mShowFocusRingForContent = false;
9751 }
9753 if (mFocusedNode) {
9754 // if a node was focused by a keypress, turn on focus rings for the
9755 // window.
9756 if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) {
9757 mFocusByKeyOccurred = true;
9758 } else if (
9759 // otherwise, we set mShowFocusRingForContent, as we don't want this to
9760 // be permanent for the window. On Windows, focus rings are only shown
9761 // when the FLAG_SHOWRING flag is used. On other platforms, focus rings
9762 // are only hidden for clicks on links.
9763 #ifndef XP_WIN
9764 !(mFocusMethod & nsIFocusManager::FLAG_BYMOUSE) || !IsLink(aNode) ||
9765 #endif
9766 aFocusMethod & nsIFocusManager::FLAG_SHOWRING) {
9767 mShowFocusRingForContent = true;
9768 }
9769 }
9771 if (aNeedsFocus)
9772 mNeedsFocus = aNeedsFocus;
9773 }
9775 uint32_t
9776 nsGlobalWindow::GetFocusMethod()
9777 {
9778 FORWARD_TO_INNER(GetFocusMethod, (), 0);
9780 return mFocusMethod;
9781 }
9783 bool
9784 nsGlobalWindow::ShouldShowFocusRing()
9785 {
9786 FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
9788 return mShowFocusRings || mShowFocusRingForContent || mFocusByKeyOccurred;
9789 }
9791 void
9792 nsGlobalWindow::SetKeyboardIndicators(UIStateChangeType aShowAccelerators,
9793 UIStateChangeType aShowFocusRings)
9794 {
9795 FORWARD_TO_INNER_VOID(SetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
9797 bool oldShouldShowFocusRing = ShouldShowFocusRing();
9799 // only change the flags that have been modified
9800 if (aShowAccelerators != UIStateChangeType_NoChange)
9801 mShowAccelerators = aShowAccelerators == UIStateChangeType_Set;
9802 if (aShowFocusRings != UIStateChangeType_NoChange)
9803 mShowFocusRings = aShowFocusRings == UIStateChangeType_Set;
9805 // propagate the indicators to child windows
9806 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
9807 if (docShell) {
9808 int32_t childCount = 0;
9809 docShell->GetChildCount(&childCount);
9811 for (int32_t i = 0; i < childCount; ++i) {
9812 nsCOMPtr<nsIDocShellTreeItem> childShell;
9813 docShell->GetChildAt(i, getter_AddRefs(childShell));
9814 nsCOMPtr<nsPIDOMWindow> childWindow = do_GetInterface(childShell);
9815 if (childWindow) {
9816 childWindow->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
9817 }
9818 }
9819 }
9821 bool newShouldShowFocusRing = ShouldShowFocusRing();
9822 if (mHasFocus && mFocusedNode &&
9823 oldShouldShowFocusRing != newShouldShowFocusRing &&
9824 mFocusedNode->IsElement()) {
9825 // Update mFocusedNode's state.
9826 if (newShouldShowFocusRing) {
9827 mFocusedNode->AsElement()->AddStates(NS_EVENT_STATE_FOCUSRING);
9828 } else {
9829 mFocusedNode->AsElement()->RemoveStates(NS_EVENT_STATE_FOCUSRING);
9830 }
9831 }
9832 }
9834 void
9835 nsGlobalWindow::GetKeyboardIndicators(bool* aShowAccelerators,
9836 bool* aShowFocusRings)
9837 {
9838 FORWARD_TO_INNER_VOID(GetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
9840 *aShowAccelerators = mShowAccelerators;
9841 *aShowFocusRings = mShowFocusRings;
9842 }
9844 bool
9845 nsGlobalWindow::TakeFocus(bool aFocus, uint32_t aFocusMethod)
9846 {
9847 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
9849 if (mCleanedUp) {
9850 return false;
9851 }
9853 if (aFocus)
9854 mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
9856 if (mHasFocus != aFocus) {
9857 mHasFocus = aFocus;
9858 UpdateCanvasFocus(true, mFocusedNode);
9859 }
9861 // if mNeedsFocus is true, then the document has not yet received a
9862 // document-level focus event. If there is a root content node, then return
9863 // true to tell the calling focus manager that a focus event is expected. If
9864 // there is no root content node, the document hasn't loaded enough yet, or
9865 // there isn't one and there is no point in firing a focus event.
9866 if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) {
9867 mNeedsFocus = false;
9868 return true;
9869 }
9871 mNeedsFocus = false;
9872 return false;
9873 }
9875 void
9876 nsGlobalWindow::SetReadyForFocus()
9877 {
9878 FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
9880 bool oldNeedsFocus = mNeedsFocus;
9881 mNeedsFocus = false;
9883 // update whether focus rings need to be shown using the state from the
9884 // root window
9885 nsPIDOMWindow* root = GetPrivateRoot();
9886 if (root) {
9887 bool showAccelerators, showFocusRings;
9888 root->GetKeyboardIndicators(&showAccelerators, &showFocusRings);
9889 mShowFocusRings = showFocusRings;
9890 }
9892 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9893 if (fm)
9894 fm->WindowShown(this, oldNeedsFocus);
9895 }
9897 void
9898 nsGlobalWindow::PageHidden()
9899 {
9900 FORWARD_TO_INNER_VOID(PageHidden, ());
9902 // the window is being hidden, so tell the focus manager that the frame is
9903 // no longer valid. Use the persisted field to determine if the document
9904 // is being destroyed.
9906 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9907 if (fm)
9908 fm->WindowHidden(this);
9910 mNeedsFocus = true;
9911 }
9913 class HashchangeCallback : public nsRunnable
9914 {
9915 public:
9916 HashchangeCallback(const nsAString &aOldURL,
9917 const nsAString &aNewURL,
9918 nsGlobalWindow* aWindow)
9919 : mWindow(aWindow)
9920 {
9921 MOZ_ASSERT(mWindow);
9922 MOZ_ASSERT(mWindow->IsInnerWindow());
9923 mOldURL.Assign(aOldURL);
9924 mNewURL.Assign(aNewURL);
9925 }
9927 NS_IMETHOD Run()
9928 {
9929 NS_PRECONDITION(NS_IsMainThread(), "Should be called on the main thread.");
9930 return mWindow->FireHashchange(mOldURL, mNewURL);
9931 }
9933 private:
9934 nsString mOldURL;
9935 nsString mNewURL;
9936 nsRefPtr<nsGlobalWindow> mWindow;
9937 };
9939 nsresult
9940 nsGlobalWindow::DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI)
9941 {
9942 FORWARD_TO_INNER(DispatchAsyncHashchange, (aOldURI, aNewURI), NS_OK);
9944 // Make sure that aOldURI and aNewURI are identical up to the '#', and that
9945 // their hashes are different.
9946 nsAutoCString oldBeforeHash, oldHash, newBeforeHash, newHash;
9947 nsContentUtils::SplitURIAtHash(aOldURI, oldBeforeHash, oldHash);
9948 nsContentUtils::SplitURIAtHash(aNewURI, newBeforeHash, newHash);
9950 NS_ENSURE_STATE(oldBeforeHash.Equals(newBeforeHash));
9951 NS_ENSURE_STATE(!oldHash.Equals(newHash));
9953 nsAutoCString oldSpec, newSpec;
9954 aOldURI->GetSpec(oldSpec);
9955 aNewURI->GetSpec(newSpec);
9957 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
9958 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
9960 nsCOMPtr<nsIRunnable> callback =
9961 new HashchangeCallback(oldWideSpec, newWideSpec, this);
9962 return NS_DispatchToMainThread(callback);
9963 }
9965 nsresult
9966 nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
9967 const nsAString &aNewURL)
9968 {
9969 MOZ_ASSERT(IsInnerWindow());
9971 // Don't do anything if the window is frozen.
9972 if (IsFrozen())
9973 return NS_OK;
9975 // Get a presentation shell for use in creating the hashchange event.
9976 NS_ENSURE_STATE(IsCurrentInnerWindow());
9978 nsIPresShell *shell = mDoc->GetShell();
9979 nsRefPtr<nsPresContext> presContext;
9980 if (shell) {
9981 presContext = shell->GetPresContext();
9982 }
9984 // Create a new hashchange event.
9985 nsCOMPtr<nsIDOMEvent> domEvent;
9986 nsresult rv =
9987 EventDispatcher::CreateEvent(this, presContext, nullptr,
9988 NS_LITERAL_STRING("hashchangeevent"),
9989 getter_AddRefs(domEvent));
9990 NS_ENSURE_SUCCESS(rv, rv);
9992 nsCOMPtr<nsIDOMHashChangeEvent> hashchangeEvent = do_QueryInterface(domEvent);
9993 NS_ENSURE_TRUE(hashchangeEvent, NS_ERROR_UNEXPECTED);
9995 // The hashchange event bubbles and isn't cancellable.
9996 rv = hashchangeEvent->InitHashChangeEvent(NS_LITERAL_STRING("hashchange"),
9997 true, false,
9998 aOldURL, aNewURL);
9999 NS_ENSURE_SUCCESS(rv, rv);
10001 domEvent->SetTrusted(true);
10003 bool dummy;
10004 return DispatchEvent(hashchangeEvent, &dummy);
10005 }
10007 nsresult
10008 nsGlobalWindow::DispatchSyncPopState()
10009 {
10010 FORWARD_TO_INNER(DispatchSyncPopState, (), NS_OK);
10012 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
10013 "Must be safe to run script here.");
10015 // Check that PopState hasn't been pref'ed off.
10016 if (!Preferences::GetBool(sPopStatePrefStr, false)) {
10017 return NS_OK;
10018 }
10020 nsresult rv = NS_OK;
10022 // Bail if the window is frozen.
10023 if (IsFrozen()) {
10024 return NS_OK;
10025 }
10027 // Get the document's pending state object -- it contains the data we're
10028 // going to send along with the popstate event. The object is serialized
10029 // using structured clone.
10030 nsCOMPtr<nsIVariant> stateObj;
10031 rv = mDoc->GetStateObject(getter_AddRefs(stateObj));
10032 NS_ENSURE_SUCCESS(rv, rv);
10034 // Obtain a presentation shell for use in creating a popstate event.
10035 nsIPresShell *shell = mDoc->GetShell();
10036 nsRefPtr<nsPresContext> presContext;
10037 if (shell) {
10038 presContext = shell->GetPresContext();
10039 }
10041 // Create a new popstate event
10042 nsCOMPtr<nsIDOMEvent> domEvent;
10043 rv = EventDispatcher::CreateEvent(this, presContext, nullptr,
10044 NS_LITERAL_STRING("popstateevent"),
10045 getter_AddRefs(domEvent));
10046 NS_ENSURE_SUCCESS(rv, rv);
10048 // Initialize the popstate event, which does bubble but isn't cancellable.
10049 nsCOMPtr<nsIDOMPopStateEvent> popstateEvent = do_QueryInterface(domEvent);
10050 rv = popstateEvent->InitPopStateEvent(NS_LITERAL_STRING("popstate"),
10051 true, false,
10052 stateObj);
10053 NS_ENSURE_SUCCESS(rv, rv);
10055 domEvent->SetTrusted(true);
10057 nsCOMPtr<EventTarget> outerWindow =
10058 do_QueryInterface(GetOuterWindow());
10059 NS_ENSURE_TRUE(outerWindow, NS_ERROR_UNEXPECTED);
10061 rv = domEvent->SetTarget(outerWindow);
10062 NS_ENSURE_SUCCESS(rv, rv);
10064 bool dummy; // default action
10065 return DispatchEvent(popstateEvent, &dummy);
10066 }
10068 // Find an nsICanvasFrame under aFrame. Only search the principal
10069 // child lists. aFrame must be non-null.
10070 static nsCanvasFrame* FindCanvasFrame(nsIFrame* aFrame)
10071 {
10072 nsCanvasFrame* canvasFrame = do_QueryFrame(aFrame);
10073 if (canvasFrame) {
10074 return canvasFrame;
10075 }
10077 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
10078 while (kid) {
10079 canvasFrame = FindCanvasFrame(kid);
10080 if (canvasFrame) {
10081 return canvasFrame;
10082 }
10083 kid = kid->GetNextSibling();
10084 }
10086 return nullptr;
10087 }
10089 //-------------------------------------------------------
10090 // Tells the HTMLFrame/CanvasFrame that is now has focus
10091 void
10092 nsGlobalWindow::UpdateCanvasFocus(bool aFocusChanged, nsIContent* aNewContent)
10093 {
10094 MOZ_ASSERT(IsInnerWindow());
10096 // this is called from the inner window so use GetDocShell
10097 nsIDocShell* docShell = GetDocShell();
10098 if (!docShell)
10099 return;
10101 bool editable;
10102 docShell->GetEditable(&editable);
10103 if (editable)
10104 return;
10106 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
10107 if (!presShell || !mDoc)
10108 return;
10110 Element *rootElement = mDoc->GetRootElement();
10111 if (rootElement) {
10112 if ((mHasFocus || aFocusChanged) &&
10113 (mFocusedNode == rootElement || aNewContent == rootElement)) {
10114 nsIFrame* frame = rootElement->GetPrimaryFrame();
10115 if (frame) {
10116 frame = frame->GetParent();
10117 nsCanvasFrame* canvasFrame = do_QueryFrame(frame);
10118 if (canvasFrame) {
10119 canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent);
10120 }
10121 }
10122 }
10123 } else {
10124 // Look for the frame the hard way
10125 nsIFrame* frame = presShell->GetRootFrame();
10126 if (frame) {
10127 nsCanvasFrame* canvasFrame = FindCanvasFrame(frame);
10128 if (canvasFrame) {
10129 canvasFrame->SetHasFocus(false);
10130 }
10131 }
10132 }
10133 }
10135 already_AddRefed<nsICSSDeclaration>
10136 nsGlobalWindow::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt,
10137 ErrorResult& aError)
10138 {
10139 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
10140 }
10142 NS_IMETHODIMP
10143 nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt,
10144 const nsAString& aPseudoElt,
10145 nsIDOMCSSStyleDeclaration** aReturn)
10146 {
10147 return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn);
10148 }
10150 already_AddRefed<nsICSSDeclaration>
10151 nsGlobalWindow::GetDefaultComputedStyle(Element& aElt,
10152 const nsAString& aPseudoElt,
10153 ErrorResult& aError)
10154 {
10155 return GetComputedStyleHelper(aElt, aPseudoElt, true, aError);
10156 }
10158 NS_IMETHODIMP
10159 nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt,
10160 const nsAString& aPseudoElt,
10161 nsIDOMCSSStyleDeclaration** aReturn)
10162 {
10163 return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn);
10164 }
10166 nsresult
10167 nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt,
10168 const nsAString& aPseudoElt,
10169 bool aDefaultStylesOnly,
10170 nsIDOMCSSStyleDeclaration** aReturn)
10171 {
10172 NS_ENSURE_ARG_POINTER(aReturn);
10173 *aReturn = nullptr;
10175 nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
10176 if (!element) {
10177 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
10178 }
10180 ErrorResult rv;
10181 nsCOMPtr<nsIDOMCSSStyleDeclaration> declaration =
10182 GetComputedStyleHelper(*element, aPseudoElt, aDefaultStylesOnly, rv);
10183 declaration.forget(aReturn);
10185 return rv.ErrorCode();
10186 }
10188 already_AddRefed<nsICSSDeclaration>
10189 nsGlobalWindow::GetComputedStyleHelper(Element& aElt,
10190 const nsAString& aPseudoElt,
10191 bool aDefaultStylesOnly,
10192 ErrorResult& aError)
10193 {
10194 FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelper,
10195 (aElt, aPseudoElt, aDefaultStylesOnly, aError),
10196 aError, nullptr);
10198 if (!mDocShell) {
10199 return nullptr;
10200 }
10202 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
10204 if (!presShell) {
10205 // Try flushing frames on our parent in case there's a pending
10206 // style change that will create the presshell.
10207 nsGlobalWindow *parent =
10208 static_cast<nsGlobalWindow *>(GetPrivateParent());
10209 if (!parent) {
10210 return nullptr;
10211 }
10213 parent->FlushPendingNotifications(Flush_Frames);
10215 // Might have killed mDocShell
10216 if (!mDocShell) {
10217 return nullptr;
10218 }
10220 presShell = mDocShell->GetPresShell();
10221 if (!presShell) {
10222 return nullptr;
10223 }
10224 }
10226 nsRefPtr<nsComputedDOMStyle> compStyle =
10227 NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell,
10228 aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
10229 nsComputedDOMStyle::eAll);
10231 return compStyle.forget();
10232 }
10234 nsIDOMStorage*
10235 nsGlobalWindow::GetSessionStorage(ErrorResult& aError)
10236 {
10237 FORWARD_TO_INNER_OR_THROW(GetSessionStorage, (aError), aError, nullptr);
10239 nsIPrincipal *principal = GetPrincipal();
10240 nsIDocShell* docShell = GetDocShell();
10242 if (!principal || !docShell || !Preferences::GetBool(kStorageEnabled)) {
10243 return nullptr;
10244 }
10246 if (mSessionStorage) {
10247 #ifdef PR_LOGGING
10248 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
10249 PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get());
10250 }
10251 #endif
10252 nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(mSessionStorage);
10253 if (piStorage) {
10254 bool canAccess = piStorage->CanAccess(principal);
10255 NS_ASSERTION(canAccess,
10256 "window %x owned sessionStorage "
10257 "that could not be accessed!");
10258 if (!canAccess) {
10259 mSessionStorage = nullptr;
10260 }
10261 }
10262 }
10264 if (!mSessionStorage) {
10265 nsString documentURI;
10266 if (mDoc) {
10267 mDoc->GetDocumentURI(documentURI);
10268 }
10270 // If the document has the sandboxed origin flag set
10271 // don't allow access to sessionStorage.
10272 if (!mDoc) {
10273 aError.Throw(NS_ERROR_FAILURE);
10274 return nullptr;
10275 }
10277 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
10278 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10279 return nullptr;
10280 }
10282 nsresult rv;
10284 nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
10285 if (NS_FAILED(rv)) {
10286 aError.Throw(rv);
10287 return nullptr;
10288 }
10290 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
10292 nsCOMPtr<nsIURI> firstPartyIsolationURI;
10293 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
10294 if (NS_FAILED(rv)) {
10295 aError.Throw(rv);
10296 return nullptr;
10297 }
10299 aError = storageManager->CreateStorageForFirstParty(firstPartyIsolationURI, principal,
10300 documentURI,
10301 loadContext && loadContext->UsePrivateBrowsing(),
10302 getter_AddRefs(mSessionStorage));
10303 if (aError.Failed()) {
10304 return nullptr;
10305 }
10307 #ifdef PR_LOGGING
10308 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
10309 PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get());
10310 }
10311 #endif
10313 if (!mSessionStorage) {
10314 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10315 return nullptr;
10316 }
10317 }
10319 #ifdef PR_LOGGING
10320 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
10321 PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
10322 }
10323 #endif
10325 return mSessionStorage;
10326 }
10328 NS_IMETHODIMP
10329 nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
10330 {
10331 ErrorResult rv;
10332 nsCOMPtr<nsIDOMStorage> storage = GetSessionStorage(rv);
10333 storage.forget(aSessionStorage);
10335 return rv.ErrorCode();
10336 }
10338 nsIDOMStorage*
10339 nsGlobalWindow::GetLocalStorage(ErrorResult& aError)
10340 {
10341 FORWARD_TO_INNER_OR_THROW(GetLocalStorage, (aError), aError, nullptr);
10343 if (!Preferences::GetBool(kStorageEnabled)) {
10344 return nullptr;
10345 }
10347 if (!mLocalStorage) {
10348 if (!DOMStorage::CanUseStorage()) {
10349 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10350 return nullptr;
10351 }
10353 nsIPrincipal *principal = GetPrincipal();
10354 if (!principal) {
10355 return nullptr;
10356 }
10358 nsresult rv;
10359 nsCOMPtr<nsIDOMStorageManager> storageManager =
10360 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
10361 if (NS_FAILED(rv)) {
10362 aError.Throw(rv);
10363 return nullptr;
10364 }
10366 // If the document has the sandboxed origin flag set
10367 // don't allow access to localStorage.
10368 if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
10369 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10370 return nullptr;
10371 }
10373 nsString documentURI;
10374 if (mDoc) {
10375 mDoc->GetDocumentURI(documentURI);
10376 }
10378 nsCOMPtr<nsIURI> firstPartyIsolationURI;
10379 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
10380 if (NS_FAILED(rv)) {
10381 aError.Throw(rv);
10382 return nullptr;
10383 }
10385 nsIDocShell* docShell = GetDocShell();
10386 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
10388 aError = storageManager->CreateStorageForFirstParty(firstPartyIsolationURI, principal,
10389 documentURI,
10390 loadContext && loadContext->UsePrivateBrowsing(),
10391 getter_AddRefs(mLocalStorage));
10392 }
10394 return mLocalStorage;
10395 }
10397 NS_IMETHODIMP
10398 nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
10399 {
10400 NS_ENSURE_ARG(aLocalStorage);
10402 ErrorResult rv;
10403 nsCOMPtr<nsIDOMStorage> storage = GetLocalStorage(rv);
10404 storage.forget(aLocalStorage);
10406 return rv.ErrorCode();
10407 }
10409 indexedDB::IDBFactory*
10410 nsGlobalWindow::GetIndexedDB(ErrorResult& aError)
10411 {
10412 if (!mIndexedDB) {
10413 // If the document has the sandboxed origin flag set
10414 // don't allow access to indexedDB.
10415 if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
10416 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10417 return nullptr;
10418 }
10420 if (!IsChromeWindow()) {
10421 // Whitelist about:home, since it doesn't have a base domain it would not
10422 // pass the thirdPartyUtil check, though it should be able to use
10423 // indexedDB.
10424 bool skipThirdPartyCheck = false;
10425 nsIPrincipal *principal = GetPrincipal();
10426 if (principal) {
10427 nsCOMPtr<nsIURI> uri;
10428 principal->GetURI(getter_AddRefs(uri));
10429 bool isAbout = false;
10430 if (uri && NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
10431 nsAutoCString path;
10432 skipThirdPartyCheck = NS_SUCCEEDED(uri->GetPath(path)) &&
10433 path.EqualsLiteral("home");
10434 }
10435 }
10437 if (!skipThirdPartyCheck) {
10438 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
10439 do_GetService(THIRDPARTYUTIL_CONTRACTID);
10440 if (!thirdPartyUtil) {
10441 aError.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
10442 return nullptr;
10443 }
10445 bool isThirdParty;
10446 aError = thirdPartyUtil->IsThirdPartyWindow(this, nullptr,
10447 &isThirdParty);
10448 if (aError.Failed() || isThirdParty) {
10449 NS_WARN_IF_FALSE(aError.Failed(),
10450 "IndexedDB is not permitted in a third-party window.");
10451 return nullptr;
10452 }
10453 }
10454 }
10456 // This may be null if being created from a file.
10457 aError = indexedDB::IDBFactory::Create(this, nullptr,
10458 getter_AddRefs(mIndexedDB));
10459 }
10461 return mIndexedDB;
10462 }
10464 NS_IMETHODIMP
10465 nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
10466 {
10467 ErrorResult rv;
10468 nsCOMPtr<nsISupports> request(GetIndexedDB(rv));
10469 request.forget(_retval);
10471 return rv.ErrorCode();
10472 }
10474 NS_IMETHODIMP
10475 nsGlobalWindow::GetMozIndexedDB(nsISupports** _retval)
10476 {
10477 return GetIndexedDB(_retval);
10478 }
10480 //*****************************************************************************
10481 // nsGlobalWindow::nsIInterfaceRequestor
10482 //*****************************************************************************
10484 NS_IMETHODIMP
10485 nsGlobalWindow::GetInterface(const nsIID & aIID, void **aSink)
10486 {
10487 NS_ENSURE_ARG_POINTER(aSink);
10488 *aSink = nullptr;
10490 if (aIID.Equals(NS_GET_IID(nsIDocCharset))) {
10491 nsGlobalWindow* outer = GetOuterWindowInternal();
10492 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10494 NS_WARNING("Using deprecated nsIDocCharset: use nsIDocShell.GetCharset() instead ");
10495 nsCOMPtr<nsIDocCharset> docCharset(do_QueryInterface(outer->mDocShell));
10496 docCharset.forget(aSink);
10497 }
10498 else if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) {
10499 nsGlobalWindow* outer = GetOuterWindowInternal();
10500 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10502 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(outer->mDocShell));
10503 webNav.forget(aSink);
10504 }
10505 else if (aIID.Equals(NS_GET_IID(nsIDocShell))) {
10506 nsGlobalWindow* outer = GetOuterWindowInternal();
10507 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10509 nsCOMPtr<nsIDocShell> docShell = outer->mDocShell;
10510 docShell.forget(aSink);
10511 }
10512 #ifdef NS_PRINTING
10513 else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) {
10514 nsGlobalWindow* outer = GetOuterWindowInternal();
10515 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10517 if (outer->mDocShell) {
10518 nsCOMPtr<nsIContentViewer> viewer;
10519 outer->mDocShell->GetContentViewer(getter_AddRefs(viewer));
10520 if (viewer) {
10521 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer));
10522 webBrowserPrint.forget(aSink);
10523 }
10524 }
10525 }
10526 #endif
10527 else if (aIID.Equals(NS_GET_IID(nsIDOMWindowUtils))) {
10528 nsGlobalWindow* outer = GetOuterWindowInternal();
10529 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10531 if (!mWindowUtils) {
10532 mWindowUtils = new nsDOMWindowUtils(outer);
10533 }
10535 *aSink = mWindowUtils;
10536 NS_ADDREF(((nsISupports *) *aSink));
10537 }
10538 else if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
10539 nsGlobalWindow* outer = GetOuterWindowInternal();
10540 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10542 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(outer->mDocShell));
10543 loadContext.forget(aSink);
10544 }
10545 else {
10546 return QueryInterface(aIID, aSink);
10547 }
10549 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
10550 }
10552 void
10553 nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID,
10554 JS::MutableHandle<JS::Value> aRetval,
10555 ErrorResult& aError)
10556 {
10557 dom::GetInterface(aCx, this, aIID, aRetval, aError);
10558 }
10560 void
10561 nsGlobalWindow::FireOfflineStatusEvent()
10562 {
10563 if (!IsCurrentInnerWindow())
10564 return;
10565 nsAutoString name;
10566 if (NS_IsOffline()) {
10567 name.AssignLiteral("offline");
10568 } else {
10569 name.AssignLiteral("online");
10570 }
10571 // The event is fired at the body element, or if there is no body element,
10572 // at the document.
10573 nsCOMPtr<EventTarget> eventTarget = mDoc.get();
10574 nsHTMLDocument* htmlDoc = mDoc->AsHTMLDocument();
10575 if (htmlDoc) {
10576 Element* body = htmlDoc->GetBody();
10577 if (body) {
10578 eventTarget = body;
10579 }
10580 } else {
10581 Element* documentElement = mDoc->GetDocumentElement();
10582 if (documentElement) {
10583 eventTarget = documentElement;
10584 }
10585 }
10586 nsContentUtils::DispatchTrustedEvent(mDoc, eventTarget, name, true, false);
10587 }
10589 class NotifyIdleObserverRunnable : public nsRunnable
10590 {
10591 public:
10592 NotifyIdleObserverRunnable(nsIIdleObserver* aIdleObserver,
10593 uint32_t aTimeInS,
10594 bool aCallOnidle,
10595 nsGlobalWindow* aIdleWindow)
10596 : mIdleObserver(aIdleObserver), mTimeInS(aTimeInS), mIdleWindow(aIdleWindow),
10597 mCallOnidle(aCallOnidle)
10598 { }
10600 NS_IMETHOD Run()
10601 {
10602 if (mIdleWindow->ContainsIdleObserver(mIdleObserver, mTimeInS)) {
10603 return mCallOnidle ? mIdleObserver->Onidle() : mIdleObserver->Onactive();
10604 }
10605 return NS_OK;
10606 }
10608 private:
10609 nsCOMPtr<nsIIdleObserver> mIdleObserver;
10610 uint32_t mTimeInS;
10611 nsRefPtr<nsGlobalWindow> mIdleWindow;
10613 // If false then call on active
10614 bool mCallOnidle;
10615 };
10617 void
10618 nsGlobalWindow::NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
10619 bool aCallOnidle)
10620 {
10621 MOZ_ASSERT(aIdleObserverHolder);
10622 aIdleObserverHolder->mPrevNotificationIdle = aCallOnidle;
10624 nsCOMPtr<nsIRunnable> caller =
10625 new NotifyIdleObserverRunnable(aIdleObserverHolder->mIdleObserver,
10626 aIdleObserverHolder->mTimeInS,
10627 aCallOnidle, this);
10628 if (NS_FAILED(NS_DispatchToCurrentThread(caller))) {
10629 NS_WARNING("Failed to dispatch thread for idle observer notification.");
10630 }
10631 }
10633 bool
10634 nsGlobalWindow::ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t aTimeInS)
10635 {
10636 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
10637 bool found = false;
10638 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
10639 while (iter.HasMore()) {
10640 IdleObserverHolder& idleObserver = iter.GetNext();
10641 if (idleObserver.mIdleObserver == aIdleObserver &&
10642 idleObserver.mTimeInS == aTimeInS) {
10643 found = true;
10644 break;
10645 }
10646 }
10647 return found;
10648 }
10650 void
10651 IdleActiveTimerCallback(nsITimer* aTimer, void* aClosure)
10652 {
10653 nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure);
10654 MOZ_ASSERT(idleWindow, "Idle window has not been instantiated.");
10655 idleWindow->HandleIdleActiveEvent();
10656 }
10658 void
10659 IdleObserverTimerCallback(nsITimer* aTimer, void* aClosure)
10660 {
10661 nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure);
10662 MOZ_ASSERT(idleWindow, "Idle window has not been instantiated.");
10663 idleWindow->HandleIdleObserverCallback();
10664 }
10666 void
10667 nsGlobalWindow::HandleIdleObserverCallback()
10668 {
10669 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10670 MOZ_ASSERT(static_cast<uint32_t>(mIdleCallbackIndex) < mIdleObservers.Length(),
10671 "Idle callback index exceeds array bounds!");
10672 IdleObserverHolder& idleObserver = mIdleObservers.ElementAt(mIdleCallbackIndex);
10673 NotifyIdleObserver(&idleObserver, true);
10674 mIdleCallbackIndex++;
10675 if (NS_FAILED(ScheduleNextIdleObserverCallback())) {
10676 NS_WARNING("Failed to set next idle observer callback.");
10677 }
10678 }
10680 nsresult
10681 nsGlobalWindow::ScheduleNextIdleObserverCallback()
10682 {
10683 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10684 MOZ_ASSERT(mIdleService, "No idle service!");
10686 if (mIdleCallbackIndex < 0 ||
10687 static_cast<uint32_t>(mIdleCallbackIndex) >= mIdleObservers.Length()) {
10688 return NS_OK;
10689 }
10691 IdleObserverHolder& idleObserver =
10692 mIdleObservers.ElementAt(mIdleCallbackIndex);
10694 uint32_t userIdleTimeMS = 0;
10695 nsresult rv = mIdleService->GetIdleTime(&userIdleTimeMS);
10696 NS_ENSURE_SUCCESS(rv, rv);
10698 uint32_t callbackTimeMS = 0;
10699 if (idleObserver.mTimeInS * 1000 + mIdleFuzzFactor > userIdleTimeMS) {
10700 callbackTimeMS = idleObserver.mTimeInS * 1000 - userIdleTimeMS + mIdleFuzzFactor;
10701 }
10703 mIdleTimer->Cancel();
10704 rv = mIdleTimer->InitWithFuncCallback(IdleObserverTimerCallback,
10705 this,
10706 callbackTimeMS,
10707 nsITimer::TYPE_ONE_SHOT);
10708 NS_ENSURE_SUCCESS(rv, rv);
10710 return NS_OK;
10711 }
10713 uint32_t
10714 nsGlobalWindow::GetFuzzTimeMS()
10715 {
10716 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10718 if (sIdleObserversAPIFuzzTimeDisabled) {
10719 return 0;
10720 }
10722 uint32_t randNum = MAX_IDLE_FUZZ_TIME_MS;
10723 size_t nbytes = PR_GetRandomNoise(&randNum, sizeof(randNum));
10724 if (nbytes != sizeof(randNum)) {
10725 NS_WARNING("PR_GetRandomNoise(...) Not implemented or no available noise!");
10726 return MAX_IDLE_FUZZ_TIME_MS;
10727 }
10729 if (randNum > MAX_IDLE_FUZZ_TIME_MS) {
10730 randNum %= MAX_IDLE_FUZZ_TIME_MS;
10731 }
10733 return randNum;
10734 }
10736 nsresult
10737 nsGlobalWindow::ScheduleActiveTimerCallback()
10738 {
10739 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10741 if (!mAddActiveEventFuzzTime) {
10742 return HandleIdleActiveEvent();
10743 }
10745 MOZ_ASSERT(mIdleTimer);
10746 mIdleTimer->Cancel();
10748 uint32_t fuzzFactorInMS = GetFuzzTimeMS();
10749 nsresult rv = mIdleTimer->InitWithFuncCallback(IdleActiveTimerCallback,
10750 this,
10751 fuzzFactorInMS,
10752 nsITimer::TYPE_ONE_SHOT);
10753 NS_ENSURE_SUCCESS(rv, rv);
10754 return NS_OK;
10755 }
10757 nsresult
10758 nsGlobalWindow::HandleIdleActiveEvent()
10759 {
10760 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10762 if (mCurrentlyIdle) {
10763 mIdleCallbackIndex = 0;
10764 mIdleFuzzFactor = GetFuzzTimeMS();
10765 nsresult rv = ScheduleNextIdleObserverCallback();
10766 NS_ENSURE_SUCCESS(rv, rv);
10767 return NS_OK;
10768 }
10770 mIdleCallbackIndex = -1;
10771 MOZ_ASSERT(mIdleTimer);
10772 mIdleTimer->Cancel();
10773 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
10774 while (iter.HasMore()) {
10775 IdleObserverHolder& idleObserver = iter.GetNext();
10776 if (idleObserver.mPrevNotificationIdle) {
10777 NotifyIdleObserver(&idleObserver, false);
10778 }
10779 }
10781 return NS_OK;
10782 }
10784 nsGlobalWindow::SlowScriptResponse
10785 nsGlobalWindow::ShowSlowScriptDialog()
10786 {
10787 MOZ_ASSERT(IsInnerWindow());
10789 nsresult rv;
10790 AutoJSContext cx;
10792 // If it isn't safe to run script, then it isn't safe to bring up the prompt
10793 // (since that spins the event loop). In that (rare) case, we just kill the
10794 // script and report a warning.
10795 if (!nsContentUtils::IsSafeToRunScript()) {
10796 JS_ReportWarning(cx, "A long running script was terminated");
10797 return KillSlowScript;
10798 }
10800 // If our document is not active, just kill the script: we've been unloaded
10801 if (!HasActiveDocument()) {
10802 return KillSlowScript;
10803 }
10805 // Get the nsIPrompt interface from the docshell
10806 nsCOMPtr<nsIDocShell> ds = GetDocShell();
10807 NS_ENSURE_TRUE(ds, KillSlowScript);
10808 nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
10809 NS_ENSURE_TRUE(prompt, KillSlowScript);
10811 // Check if we should offer the option to debug
10812 JS::AutoFilename filename;
10813 unsigned lineno;
10814 bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);
10816 bool debugPossible = hasFrame && js::CanCallContextDebugHandler(cx);
10817 #ifdef MOZ_JSDEBUGGER
10818 // Get the debugger service if necessary.
10819 if (debugPossible) {
10820 bool jsds_IsOn = false;
10821 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
10822 nsCOMPtr<jsdIExecutionHook> jsdHook;
10823 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
10825 // Check if there's a user for the debugger service that's 'on' for us
10826 if (NS_SUCCEEDED(rv)) {
10827 jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
10828 jsds->GetIsOn(&jsds_IsOn);
10829 }
10831 // If there is a debug handler registered for this runtime AND
10832 // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
10833 // then something useful will be done with our request to debug.
10834 debugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
10835 }
10836 #endif
10838 // Get localizable strings
10839 nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
10841 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10842 "KillScriptTitle",
10843 title);
10845 nsresult tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10846 "StopScriptButton",
10847 stopButton);
10848 if (NS_FAILED(tmp)) {
10849 rv = tmp;
10850 }
10852 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10853 "WaitForScriptButton",
10854 waitButton);
10855 if (NS_FAILED(tmp)) {
10856 rv = tmp;
10857 }
10859 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10860 "DontAskAgain",
10861 neverShowDlg);
10862 if (NS_FAILED(tmp)) {
10863 rv = tmp;
10864 }
10867 if (debugPossible) {
10868 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10869 "DebugScriptButton",
10870 debugButton);
10871 if (NS_FAILED(tmp)) {
10872 rv = tmp;
10873 }
10875 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10876 "KillScriptWithDebugMessage",
10877 msg);
10878 if (NS_FAILED(tmp)) {
10879 rv = tmp;
10880 }
10881 }
10882 else {
10883 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10884 "KillScriptMessage",
10885 msg);
10886 if (NS_FAILED(tmp)) {
10887 rv = tmp;
10888 }
10889 }
10891 // GetStringFromName can return NS_OK and still give nullptr string
10892 if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
10893 (!debugButton && debugPossible) || !neverShowDlg) {
10894 NS_ERROR("Failed to get localized strings.");
10895 return ContinueSlowScript;
10896 }
10898 // Append file and line number information, if available
10899 if (filename.get()) {
10900 nsXPIDLString scriptLocation;
10901 NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
10902 const char16_t *formatParams[] = { filenameUTF16.get() };
10903 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10904 "KillScriptLocation",
10905 formatParams,
10906 scriptLocation);
10908 if (NS_SUCCEEDED(rv) && scriptLocation) {
10909 msg.AppendLiteral("\n\n");
10910 msg.Append(scriptLocation);
10911 msg.Append(':');
10912 msg.AppendInt(lineno);
10913 }
10914 }
10916 int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
10917 bool neverShowDlgChk = false;
10918 uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
10919 (nsIPrompt::BUTTON_TITLE_IS_STRING *
10920 (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
10922 // Add a third button if necessary.
10923 if (debugPossible)
10924 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
10926 // Null out the operation callback while we're re-entering JS here.
10927 JSRuntime* rt = JS_GetRuntime(cx);
10928 JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr);
10930 // Open the dialog.
10931 rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
10932 debugButton, neverShowDlg, &neverShowDlgChk,
10933 &buttonPressed);
10935 JS_SetInterruptCallback(rt, old);
10937 if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
10938 return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
10939 }
10940 if ((buttonPressed == 2) && debugPossible) {
10941 return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
10942 }
10943 JS_ClearPendingException(cx);
10944 return KillSlowScript;
10945 }
10947 uint32_t
10948 nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver)
10949 {
10950 MOZ_ASSERT(IsInnerWindow());
10951 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
10953 uint32_t i = 0;
10954 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
10955 while (iter.HasMore()) {
10956 IdleObserverHolder& idleObserver = iter.GetNext();
10957 if (idleObserver.mTimeInS > aIdleObserver->mTimeInS) {
10958 break;
10959 }
10960 i++;
10961 MOZ_ASSERT(i <= mIdleObservers.Length(), "Array index out of bounds error.");
10962 }
10964 return i;
10965 }
10967 nsresult
10968 nsGlobalWindow::RegisterIdleObserver(nsIIdleObserver* aIdleObserver)
10969 {
10970 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10972 nsresult rv;
10973 if (mIdleObservers.IsEmpty()) {
10974 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1", &rv);
10975 NS_ENSURE_SUCCESS(rv, rv);
10977 rv = mIdleService->AddIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
10978 NS_ENSURE_SUCCESS(rv, rv);
10980 if (!mIdleTimer) {
10981 mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
10982 NS_ENSURE_SUCCESS(rv, rv);
10983 } else {
10984 mIdleTimer->Cancel();
10985 }
10986 }
10988 MOZ_ASSERT(mIdleService);
10989 MOZ_ASSERT(mIdleTimer);
10991 IdleObserverHolder tmpIdleObserver;
10992 tmpIdleObserver.mIdleObserver = aIdleObserver;
10993 rv = aIdleObserver->GetTime(&tmpIdleObserver.mTimeInS);
10994 NS_ENSURE_SUCCESS(rv, rv);
10995 NS_ENSURE_ARG_MAX(tmpIdleObserver.mTimeInS, UINT32_MAX / 1000);
10996 NS_ENSURE_ARG_MIN(tmpIdleObserver.mTimeInS, MIN_IDLE_NOTIFICATION_TIME_S);
10998 uint32_t insertAtIndex = FindInsertionIndex(&tmpIdleObserver);
10999 if (insertAtIndex == mIdleObservers.Length()) {
11000 mIdleObservers.AppendElement(tmpIdleObserver);
11001 }
11002 else {
11003 mIdleObservers.InsertElementAt(insertAtIndex, tmpIdleObserver);
11004 }
11006 bool userIsIdle = false;
11007 rv = nsContentUtils::IsUserIdle(MIN_IDLE_NOTIFICATION_TIME_S, &userIsIdle);
11008 NS_ENSURE_SUCCESS(rv, rv);
11010 // Special case. First idle observer added to empty list while the user is idle.
11011 // Haven't received 'idle' topic notification from slow idle service yet.
11012 // Need to wait for the idle notification and then notify idle observers in the list.
11013 if (userIsIdle && mIdleCallbackIndex == -1) {
11014 return NS_OK;
11015 }
11017 if (!mCurrentlyIdle) {
11018 return NS_OK;
11019 }
11021 MOZ_ASSERT(mIdleCallbackIndex >= 0);
11023 if (static_cast<int32_t>(insertAtIndex) < mIdleCallbackIndex) {
11024 IdleObserverHolder& idleObserver = mIdleObservers.ElementAt(insertAtIndex);
11025 NotifyIdleObserver(&idleObserver, true);
11026 mIdleCallbackIndex++;
11027 return NS_OK;
11028 }
11030 if (static_cast<int32_t>(insertAtIndex) == mIdleCallbackIndex) {
11031 mIdleTimer->Cancel();
11032 rv = ScheduleNextIdleObserverCallback();
11033 NS_ENSURE_SUCCESS(rv, rv);
11034 }
11035 return NS_OK;
11036 }
11038 nsresult
11039 nsGlobalWindow::FindIndexOfElementToRemove(nsIIdleObserver* aIdleObserver,
11040 int32_t* aRemoveElementIndex)
11041 {
11042 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
11043 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
11045 *aRemoveElementIndex = 0;
11046 if (mIdleObservers.IsEmpty()) {
11047 return NS_ERROR_FAILURE;
11048 }
11050 uint32_t aIdleObserverTimeInS;
11051 nsresult rv = aIdleObserver->GetTime(&aIdleObserverTimeInS);
11052 NS_ENSURE_SUCCESS(rv, rv);
11053 NS_ENSURE_ARG_MIN(aIdleObserverTimeInS, MIN_IDLE_NOTIFICATION_TIME_S);
11055 nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
11056 while (iter.HasMore()) {
11057 IdleObserverHolder& idleObserver = iter.GetNext();
11058 if (idleObserver.mTimeInS == aIdleObserverTimeInS &&
11059 idleObserver.mIdleObserver == aIdleObserver ) {
11060 break;
11061 }
11062 (*aRemoveElementIndex)++;
11063 }
11064 return static_cast<uint32_t>(*aRemoveElementIndex) >= mIdleObservers.Length() ?
11065 NS_ERROR_FAILURE : NS_OK;
11066 }
11068 nsresult
11069 nsGlobalWindow::UnregisterIdleObserver(nsIIdleObserver* aIdleObserver)
11070 {
11071 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
11073 int32_t removeElementIndex;
11074 nsresult rv = FindIndexOfElementToRemove(aIdleObserver, &removeElementIndex);
11075 if (NS_FAILED(rv)) {
11076 NS_WARNING("Idle observer not found in list of idle observers. No idle observer removed.");
11077 return NS_OK;
11078 }
11079 mIdleObservers.RemoveElementAt(removeElementIndex);
11081 MOZ_ASSERT(mIdleTimer);
11082 if (mIdleObservers.IsEmpty() && mIdleService) {
11083 rv = mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
11084 NS_ENSURE_SUCCESS(rv, rv);
11085 mIdleService = nullptr;
11087 mIdleTimer->Cancel();
11088 mIdleCallbackIndex = -1;
11089 return NS_OK;
11090 }
11092 if (!mCurrentlyIdle) {
11093 return NS_OK;
11094 }
11096 if (removeElementIndex < mIdleCallbackIndex) {
11097 mIdleCallbackIndex--;
11098 return NS_OK;
11099 }
11101 if (removeElementIndex != mIdleCallbackIndex) {
11102 return NS_OK;
11103 }
11105 mIdleTimer->Cancel();
11107 // If the last element in the array had been notified then decrement
11108 // mIdleCallbackIndex because an idle was removed from the list of
11109 // idle observers.
11110 // Example: add idle observer with time 1, 2, 3,
11111 // Idle notifications for idle observers with time 1, 2, 3 are complete
11112 // Remove idle observer with time 3 while the user is still idle.
11113 // The user never transitioned to active state.
11114 // Add an idle observer with idle time 4
11115 if (static_cast<uint32_t>(mIdleCallbackIndex) == mIdleObservers.Length()) {
11116 mIdleCallbackIndex--;
11117 }
11118 rv = ScheduleNextIdleObserverCallback();
11119 NS_ENSURE_SUCCESS(rv, rv);
11121 return NS_OK;
11122 }
11124 nsresult
11125 nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
11126 const char16_t* aData)
11127 {
11128 if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
11129 if (IsFrozen()) {
11130 // if an even number of notifications arrive while we're frozen,
11131 // we don't need to fire.
11132 mFireOfflineStatusChangeEventOnThaw = !mFireOfflineStatusChangeEventOnThaw;
11133 } else {
11134 FireOfflineStatusEvent();
11135 }
11136 return NS_OK;
11137 }
11139 if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE)) {
11140 mCurrentlyIdle = true;
11141 if (IsFrozen()) {
11142 // need to fire only one idle event while the window is frozen.
11143 mNotifyIdleObserversIdleOnThaw = true;
11144 mNotifyIdleObserversActiveOnThaw = false;
11145 } else if (IsCurrentInnerWindow()) {
11146 HandleIdleActiveEvent();
11147 }
11148 return NS_OK;
11149 }
11151 if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) {
11152 mCurrentlyIdle = false;
11153 if (IsFrozen()) {
11154 mNotifyIdleObserversActiveOnThaw = true;
11155 mNotifyIdleObserversIdleOnThaw = false;
11156 } else if (IsCurrentInnerWindow()) {
11157 MOZ_ASSERT(IsInnerWindow());
11158 ScheduleActiveTimerCallback();
11159 }
11160 return NS_OK;
11161 }
11163 if (!nsCRT::strcmp(aTopic, "dom-storage2-changed")) {
11164 if (!IsInnerWindow() || !IsCurrentInnerWindow()) {
11165 return NS_OK;
11166 }
11168 nsIPrincipal *principal;
11169 nsresult rv;
11171 nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv);
11172 NS_ENSURE_SUCCESS(rv, rv);
11174 nsCOMPtr<nsIDOMStorage> changingStorage;
11175 rv = event->GetStorageArea(getter_AddRefs(changingStorage));
11176 NS_ENSURE_SUCCESS(rv, rv);
11178 bool fireMozStorageChanged = false;
11179 principal = GetPrincipal();
11180 if (!principal) {
11181 return NS_OK;
11182 }
11184 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
11186 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
11187 bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
11188 if (pistorage->IsPrivate() != isPrivate) {
11189 return NS_OK;
11190 }
11192 switch (pistorage->GetType())
11193 {
11194 case nsPIDOMStorage::SessionStorage:
11195 {
11196 bool check = false;
11198 nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
11199 if (storageManager) {
11200 nsresult rv;
11201 nsCOMPtr<nsIURI> firstPartyIsolationURI;
11202 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
11203 NS_ENSURE_SUCCESS(rv, rv);
11205 rv = storageManager->CheckStorageForFirstParty(firstPartyIsolationURI,
11206 principal, changingStorage, &check);
11207 if (NS_FAILED(rv)) {
11208 return rv;
11209 }
11210 }
11212 if (!check) {
11213 // This storage event is not coming from our storage or is coming
11214 // from a different docshell, i.e. it is a clone, ignore this event.
11215 return NS_OK;
11216 }
11218 #ifdef PR_LOGGING
11219 if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
11220 PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p", this, mSessionStorage.get(), pistorage.get());
11221 }
11222 #endif
11224 fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
11225 break;
11226 }
11228 case nsPIDOMStorage::LocalStorage:
11229 {
11230 // Allow event fire only for the same principal storages
11231 // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
11232 nsIPrincipal* storagePrincipal = pistorage->GetPrincipal();
11234 bool equals = false;
11235 rv = storagePrincipal->Equals(principal, &equals);
11236 NS_ENSURE_SUCCESS(rv, rv);
11238 if (!equals)
11239 return NS_OK;
11241 fireMozStorageChanged = SameCOMIdentity(mLocalStorage, changingStorage);
11242 break;
11243 }
11244 default:
11245 return NS_OK;
11246 }
11248 // Clone the storage event included in the observer notification. We want
11249 // to dispatch clones rather than the original event.
11250 rv = CloneStorageEvent(fireMozStorageChanged ?
11251 NS_LITERAL_STRING("MozStorageChanged") :
11252 NS_LITERAL_STRING("storage"),
11253 event);
11254 NS_ENSURE_SUCCESS(rv, rv);
11256 event->SetTrusted(true);
11258 if (fireMozStorageChanged) {
11259 WidgetEvent* internalEvent = event->GetInternalNSEvent();
11260 internalEvent->mFlags.mOnlyChromeDispatch = true;
11261 }
11263 if (IsFrozen()) {
11264 // This window is frozen, rather than firing the events here,
11265 // store the domain in which the change happened and fire the
11266 // events if we're ever thawed.
11268 mPendingStorageEvents.AppendObject(event);
11269 return NS_OK;
11270 }
11272 bool defaultActionEnabled;
11273 DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
11275 return NS_OK;
11276 }
11278 if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
11279 if (mApplicationCache)
11280 return NS_OK;
11282 // Instantiate the application object now. It observes update belonging to
11283 // this window's document and correctly updates the applicationCache object
11284 // state.
11285 nsCOMPtr<nsIDOMOfflineResourceList> applicationCache;
11286 GetApplicationCache(getter_AddRefs(applicationCache));
11287 nsCOMPtr<nsIObserver> observer = do_QueryInterface(applicationCache);
11288 if (observer)
11289 observer->Observe(aSubject, aTopic, aData);
11291 return NS_OK;
11292 }
11294 #ifdef MOZ_B2G
11295 if (!nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC) ||
11296 !nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC)) {
11297 MOZ_ASSERT(IsInnerWindow());
11298 if (!IsCurrentInnerWindow()) {
11299 return NS_OK;
11300 }
11302 nsCOMPtr<nsIDOMEvent> event;
11303 NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
11304 nsresult rv = event->InitEvent(
11305 !nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC)
11306 ? NETWORK_UPLOAD_EVENT_NAME
11307 : NETWORK_DOWNLOAD_EVENT_NAME,
11308 false, false);
11309 NS_ENSURE_SUCCESS(rv, rv);
11311 event->SetTrusted(true);
11313 bool dummy;
11314 return DispatchEvent(event, &dummy);
11315 }
11316 #endif // MOZ_B2G
11318 NS_WARNING("unrecognized topic in nsGlobalWindow::Observe");
11319 return NS_ERROR_FAILURE;
11320 }
11322 nsresult
11323 nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
11324 nsCOMPtr<nsIDOMStorageEvent>& aEvent)
11325 {
11326 nsresult rv;
11328 bool canBubble;
11329 bool cancelable;
11330 nsAutoString key;
11331 nsAutoString oldValue;
11332 nsAutoString newValue;
11333 nsAutoString url;
11334 nsCOMPtr<nsIDOMStorage> storageArea;
11336 nsCOMPtr<nsIDOMEvent> domEvent = do_QueryInterface(aEvent, &rv);
11337 NS_ENSURE_SUCCESS(rv, rv);
11339 domEvent->GetBubbles(&canBubble);
11340 domEvent->GetCancelable(&cancelable);
11342 aEvent->GetKey(key);
11343 aEvent->GetOldValue(oldValue);
11344 aEvent->GetNewValue(newValue);
11345 aEvent->GetUrl(url);
11346 aEvent->GetStorageArea(getter_AddRefs(storageArea));
11348 NS_NewDOMStorageEvent(getter_AddRefs(domEvent), this, nullptr, nullptr);
11349 aEvent = do_QueryInterface(domEvent);
11350 return aEvent->InitStorageEvent(aType, canBubble, cancelable,
11351 key, oldValue, newValue,
11352 url, storageArea);
11353 }
11355 nsresult
11356 nsGlobalWindow::FireDelayedDOMEvents()
11357 {
11358 FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
11360 for (int32_t i = 0; i < mPendingStorageEvents.Count(); ++i) {
11361 Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr);
11362 }
11364 if (mApplicationCache) {
11365 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
11366 }
11368 if (mFireOfflineStatusChangeEventOnThaw) {
11369 mFireOfflineStatusChangeEventOnThaw = false;
11370 FireOfflineStatusEvent();
11371 }
11373 if (mNotifyIdleObserversIdleOnThaw) {
11374 mNotifyIdleObserversIdleOnThaw = false;
11375 HandleIdleActiveEvent();
11376 }
11378 if (mNotifyIdleObserversActiveOnThaw) {
11379 mNotifyIdleObserversActiveOnThaw = false;
11380 ScheduleActiveTimerCallback();
11381 }
11383 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
11384 if (docShell) {
11385 int32_t childCount = 0;
11386 docShell->GetChildCount(&childCount);
11388 for (int32_t i = 0; i < childCount; ++i) {
11389 nsCOMPtr<nsIDocShellTreeItem> childShell;
11390 docShell->GetChildAt(i, getter_AddRefs(childShell));
11391 NS_ASSERTION(childShell, "null child shell");
11393 nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell);
11394 if (pWin) {
11395 nsGlobalWindow *win =
11396 static_cast<nsGlobalWindow*>
11397 (static_cast<nsPIDOMWindow*>(pWin));
11398 win->FireDelayedDOMEvents();
11399 }
11400 }
11401 }
11403 return NS_OK;
11404 }
11406 //*****************************************************************************
11407 // nsGlobalWindow: Window Control Functions
11408 //*****************************************************************************
11410 nsIDOMWindow *
11411 nsGlobalWindow::GetParentInternal()
11412 {
11413 if (IsInnerWindow()) {
11414 nsGlobalWindow* outer = GetOuterWindowInternal();
11415 if (!outer) {
11416 NS_WARNING("No outer window available!");
11417 return nullptr;
11418 }
11419 return outer->GetParentInternal();
11420 }
11422 nsCOMPtr<nsIDOMWindow> parent;
11423 GetParent(getter_AddRefs(parent));
11425 if (parent && parent != static_cast<nsIDOMWindow *>(this)) {
11426 return parent;
11427 }
11429 return nullptr;
11430 }
11432 void
11433 nsGlobalWindow::UnblockScriptedClosing()
11434 {
11435 MOZ_ASSERT(IsOuterWindow());
11436 mBlockScriptedClosingFlag = false;
11437 }
11439 class AutoUnblockScriptClosing
11440 {
11441 private:
11442 nsRefPtr<nsGlobalWindow> mWin;
11443 public:
11444 AutoUnblockScriptClosing(nsGlobalWindow* aWin)
11445 : mWin(aWin)
11446 {
11447 MOZ_ASSERT(mWin);
11448 MOZ_ASSERT(mWin->IsOuterWindow());
11449 }
11450 ~AutoUnblockScriptClosing()
11451 {
11452 void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing;
11453 NS_DispatchToCurrentThread(NS_NewRunnableMethod(mWin, run));
11454 }
11455 };
11457 nsresult
11458 nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
11459 const nsAString& aOptions, bool aDialog,
11460 bool aContentModal, bool aCalledNoScript,
11461 bool aDoJSFixups, bool aNavigate,
11462 nsIArray *argv,
11463 nsISupports *aExtraArgument,
11464 nsIPrincipal *aCalleePrincipal,
11465 JSContext *aJSCallerContext,
11466 nsIDOMWindow **aReturn)
11467 {
11468 FORWARD_TO_OUTER(OpenInternal, (aUrl, aName, aOptions, aDialog,
11469 aContentModal, aCalledNoScript, aDoJSFixups,
11470 aNavigate, argv, aExtraArgument,
11471 aCalleePrincipal, aJSCallerContext, aReturn),
11472 NS_ERROR_NOT_INITIALIZED);
11474 #ifdef DEBUG
11475 uint32_t argc = 0;
11476 if (argv)
11477 argv->GetLength(&argc);
11478 #endif
11479 NS_PRECONDITION(!aExtraArgument || (!argv && argc == 0),
11480 "Can't pass in arguments both ways");
11481 NS_PRECONDITION(!aCalledNoScript || (!argv && argc == 0),
11482 "Can't pass JS args when called via the noscript methods");
11483 NS_PRECONDITION(!aJSCallerContext || !aCalledNoScript,
11484 "Shouldn't have caller context when called noscript");
11486 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
11488 // Calls to window.open from script should navigate.
11489 MOZ_ASSERT(aCalledNoScript || aNavigate);
11491 *aReturn = nullptr;
11493 nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome();
11494 if (!chrome) {
11495 // No chrome means we don't want to go through with this open call
11496 // -- see nsIWindowWatcher.idl
11497 return NS_ERROR_NOT_AVAILABLE;
11498 }
11500 NS_ASSERTION(mDocShell, "Must have docshell here");
11502 // Popups from apps are never blocked.
11503 bool isApp = false;
11504 if (mDoc) {
11505 isApp = mDoc->NodePrincipal()->GetAppStatus() >=
11506 nsIPrincipal::APP_STATUS_INSTALLED;
11507 }
11509 const bool checkForPopup = !nsContentUtils::IsCallerChrome() &&
11510 !isApp && !aDialog && !WindowExists(aName, !aCalledNoScript);
11512 // Note: it's very important that this be an nsXPIDLCString, since we want
11513 // .get() on it to return nullptr until we write stuff to it. The window
11514 // watcher expects a null URL string if there is no URL to load.
11515 nsXPIDLCString url;
11516 nsresult rv = NS_OK;
11518 // It's important to do this security check before determining whether this
11519 // window opening should be blocked, to ensure that we don't FireAbuseEvents
11520 // for a window opening that wouldn't have succeeded in the first place.
11521 if (!aUrl.IsEmpty()) {
11522 AppendUTF16toUTF8(aUrl, url);
11524 // It's safe to skip the security check below if we're not a dialog
11525 // because window.openDialog is not callable from content script. See bug
11526 // 56851.
11527 //
11528 // If we're not navigating, we assume that whoever *does* navigate the
11529 // window will do a security check of their own.
11530 if (url.get() && !aDialog && aNavigate)
11531 rv = SecurityCheckURL(url.get());
11532 }
11534 if (NS_FAILED(rv))
11535 return rv;
11537 PopupControlState abuseLevel = gPopupControlState;
11538 if (checkForPopup) {
11539 abuseLevel = RevisePopupAbuseLevel(abuseLevel);
11540 if (abuseLevel >= openAbused) {
11541 if (aJSCallerContext) {
11542 // If script in some other window is doing a window.open on us and
11543 // it's being blocked, then it's OK to close us afterwards, probably.
11544 // But if we're doing a window.open on ourselves and block the popup,
11545 // prevent this window from closing until after this script terminates
11546 // so that whatever popup blocker UI the app has will be visible.
11547 if (mContext == GetScriptContextFromJSContext(aJSCallerContext)) {
11548 mBlockScriptedClosingFlag = true;
11549 closeUnblocker.construct(this);
11550 }
11551 }
11553 FireAbuseEvents(true, false, aUrl, aName, aOptions);
11554 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
11555 }
11556 }
11558 nsCOMPtr<nsIDOMWindow> domReturn;
11560 nsCOMPtr<nsIWindowWatcher> wwatch =
11561 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
11562 NS_ENSURE_TRUE(wwatch, rv);
11564 NS_ConvertUTF16toUTF8 options(aOptions);
11565 NS_ConvertUTF16toUTF8 name(aName);
11567 const char *options_ptr = aOptions.IsEmpty() ? nullptr : options.get();
11568 const char *name_ptr = aName.IsEmpty() ? nullptr : name.get();
11570 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
11571 NS_ENSURE_STATE(pwwatch);
11573 {
11574 // Reset popup state while opening a window to prevent the
11575 // current state from being active the whole time a modal
11576 // dialog is open.
11577 nsAutoPopupStatePusher popupStatePusher(openAbused, true);
11579 if (!aCalledNoScript) {
11580 // We asserted at the top of this function that aNavigate is true for
11581 // !aCalledNoScript.
11582 rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
11583 /* aCalledFromScript = */ true,
11584 aDialog, aNavigate, argv,
11585 getter_AddRefs(domReturn));
11586 } else {
11587 // Force a system caller here so that the window watcher won't screw us
11588 // up. We do NOT want this case looking at the JS context on the stack
11589 // when searching. Compare comments on
11590 // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
11592 // Note: Because nsWindowWatcher is so broken, it's actually important
11593 // that we don't force a system caller here, because that screws it up
11594 // when it tries to compute the caller principal to associate with dialog
11595 // arguments. That whole setup just really needs to be rewritten. :-(
11596 Maybe<AutoNoJSAPI> nojsapi;
11597 if (!aContentModal) {
11598 nojsapi.construct();
11599 }
11602 rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
11603 /* aCalledFromScript = */ false,
11604 aDialog, aNavigate, aExtraArgument,
11605 getter_AddRefs(domReturn));
11607 }
11608 }
11610 NS_ENSURE_SUCCESS(rv, rv);
11612 // success!
11614 NS_ENSURE_TRUE(domReturn, NS_OK);
11615 domReturn.swap(*aReturn);
11617 if (aDoJSFixups) {
11618 nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn));
11619 if (!chrome_win) {
11620 // A new non-chrome window was created from a call to
11621 // window.open() from JavaScript, make sure there's a document in
11622 // the new window. We do this by simply asking the new window for
11623 // its document, this will synchronously create an empty document
11624 // if there is no document in the window.
11625 // XXXbz should this just use EnsureInnerWindow()?
11626 #ifdef DEBUG_jst
11627 {
11628 nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*aReturn));
11629 NS_ASSERTION(pidomwin->GetExtantDoc(), "No document in new window!!!");
11630 }
11631 #endif
11633 nsCOMPtr<nsIDOMDocument> doc;
11634 (*aReturn)->GetDocument(getter_AddRefs(doc));
11635 }
11636 }
11638 if (checkForPopup) {
11639 if (abuseLevel >= openControlled) {
11640 nsGlobalWindow *opened = static_cast<nsGlobalWindow *>(*aReturn);
11641 if (!opened->IsPopupSpamWindow()) {
11642 opened->SetPopupSpamWindow(true);
11643 ++gOpenPopupSpamCount;
11644 }
11645 }
11646 if (abuseLevel >= openAbused)
11647 FireAbuseEvents(false, true, aUrl, aName, aOptions);
11648 }
11650 return rv;
11651 }
11653 //*****************************************************************************
11654 // nsGlobalWindow: Timeout Functions
11655 //*****************************************************************************
11657 uint32_t sNestingLevel;
11659 nsGlobalWindow*
11660 nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError)
11661 {
11662 nsGlobalWindow* currentInner;
11663 nsGlobalWindow* forwardTo;
11664 if (IsInnerWindow()) {
11665 nsGlobalWindow* outer = GetOuterWindowInternal();
11666 currentInner = outer ? outer->GetCurrentInnerWindowInternal() : this;
11668 forwardTo = this;
11669 } else {
11670 currentInner = GetCurrentInnerWindowInternal();
11672 // This needs to forward to the inner window, but since the current
11673 // inner may not be the inner in the calling scope, we need to treat
11674 // this specially here as we don't want timeouts registered in a
11675 // dying inner window to get registered and run on the current inner
11676 // window. To get this right, we need to forward this call to the
11677 // inner window that's calling window.setTimeout().
11679 forwardTo = CallerInnerWindow();
11680 if (!forwardTo) {
11681 aError.Throw(NS_ERROR_NOT_AVAILABLE);
11682 return nullptr;
11683 }
11685 // If the caller and the callee share the same outer window, forward to the
11686 // caller inner. Else, we forward to the current inner (e.g. someone is
11687 // calling setTimeout() on a reference to some other window).
11688 if (forwardTo->GetOuterWindow() != this || !forwardTo->IsInnerWindow()) {
11689 if (!currentInner) {
11690 NS_WARNING("No inner window available!");
11691 aError.Throw(NS_ERROR_NOT_INITIALIZED);
11692 return nullptr;
11693 }
11695 return currentInner;
11696 }
11697 }
11699 // If forwardTo is not the window with an active document then we want the
11700 // call to setTimeout/Interval to be a noop, so return null but don't set an
11701 // error.
11702 return forwardTo->HasActiveDocument() ? currentInner : nullptr;
11703 }
11705 int32_t
11706 nsGlobalWindow::SetTimeout(JSContext* aCx, Function& aFunction,
11707 int32_t aTimeout,
11708 const Sequence<JS::Value>& aArguments,
11709 ErrorResult& aError)
11710 {
11711 return SetTimeoutOrInterval(aFunction, aTimeout, aArguments, false, aError);
11712 }
11714 int32_t
11715 nsGlobalWindow::SetTimeout(JSContext* aCx, const nsAString& aHandler,
11716 int32_t aTimeout,
11717 const Sequence<JS::Value>& /* unused */,
11718 ErrorResult& aError)
11719 {
11720 return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError);
11721 }
11723 static bool
11724 IsInterval(const Optional<int32_t>& aTimeout, int32_t& aResultTimeout)
11725 {
11726 if (aTimeout.WasPassed()) {
11727 aResultTimeout = aTimeout.Value();
11728 return true;
11729 }
11731 // If no interval was specified, treat this like a timeout, to avoid setting
11732 // an interval of 0 milliseconds.
11733 aResultTimeout = 0;
11734 return false;
11735 }
11737 int32_t
11738 nsGlobalWindow::SetInterval(JSContext* aCx, Function& aFunction,
11739 const Optional<int32_t>& aTimeout,
11740 const Sequence<JS::Value>& aArguments,
11741 ErrorResult& aError)
11742 {
11743 int32_t timeout;
11744 bool isInterval = IsInterval(aTimeout, timeout);
11745 return SetTimeoutOrInterval(aFunction, timeout, aArguments, isInterval,
11746 aError);
11747 }
11749 int32_t
11750 nsGlobalWindow::SetInterval(JSContext* aCx, const nsAString& aHandler,
11751 const Optional<int32_t>& aTimeout,
11752 const Sequence<JS::Value>& /* unused */,
11753 ErrorResult& aError)
11754 {
11755 int32_t timeout;
11756 bool isInterval = IsInterval(aTimeout, timeout);
11757 return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError);
11758 }
11760 nsresult
11761 nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
11762 int32_t interval,
11763 bool aIsInterval, int32_t *aReturn)
11764 {
11765 MOZ_ASSERT(IsInnerWindow());
11767 // If we don't have a document (we could have been unloaded since
11768 // the call to setTimeout was made), do nothing.
11769 if (!mDoc) {
11770 return NS_OK;
11771 }
11773 // Disallow negative intervals. If aIsInterval also disallow 0,
11774 // because we use that as a "don't repeat" flag.
11775 interval = std::max(aIsInterval ? 1 : 0, interval);
11777 // Make sure we don't proceed with an interval larger than our timer
11778 // code can handle. (Note: we already forced |interval| to be non-negative,
11779 // so the uint32_t cast (to avoid compiler warnings) is ok.)
11780 uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE);
11781 if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
11782 interval = maxTimeoutMs;
11783 }
11785 nsRefPtr<nsTimeout> timeout = new nsTimeout();
11786 timeout->mIsInterval = aIsInterval;
11787 timeout->mInterval = interval;
11788 timeout->mScriptHandler = aHandler;
11790 // Now clamp the actual interval we will use for the timer based on
11791 uint32_t nestingLevel = sNestingLevel + 1;
11792 uint32_t realInterval = interval;
11793 if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
11794 // Don't allow timeouts less than DOMMinTimeoutValue() from
11795 // now...
11796 realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
11797 }
11799 // Get principal of currently executing code, save for execution of timeout.
11800 // If our principals subsume the subject principal then use the subject
11801 // principal. Otherwise, use our principal to avoid running script in
11802 // elevated principals.
11804 nsCOMPtr<nsIPrincipal> subjectPrincipal;
11805 nsresult rv;
11806 rv = nsContentUtils::GetSecurityManager()->
11807 GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
11808 if (NS_FAILED(rv)) {
11809 return NS_ERROR_FAILURE;
11810 }
11812 bool subsumes = false;
11813 nsCOMPtr<nsIPrincipal> ourPrincipal = GetPrincipal();
11815 // Note the direction of this test: We don't allow setTimeouts running with
11816 // chrome privileges on content windows, but we do allow setTimeouts running
11817 // with content privileges on chrome windows (where they can't do very much,
11818 // of course).
11819 rv = ourPrincipal->Subsumes(subjectPrincipal, &subsumes);
11820 if (NS_FAILED(rv)) {
11821 return NS_ERROR_FAILURE;
11822 }
11824 if (subsumes) {
11825 timeout->mPrincipal = subjectPrincipal;
11826 } else {
11827 timeout->mPrincipal = ourPrincipal;
11828 }
11830 ++gTimeoutsRecentlySet;
11831 TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
11833 if (!IsFrozen() && !mTimeoutsSuspendDepth) {
11834 // If we're not currently frozen, then we set timeout->mWhen to be the
11835 // actual firing time of the timer (i.e., now + delta). We also actually
11836 // create a timer and fire it off.
11838 timeout->mWhen = TimeStamp::Now() + delta;
11840 timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
11841 if (NS_FAILED(rv)) {
11842 return rv;
11843 }
11845 nsRefPtr<nsTimeout> copy = timeout;
11847 rv = timeout->InitTimer(TimerCallback, realInterval);
11848 if (NS_FAILED(rv)) {
11849 return rv;
11850 }
11852 // The timeout is now also held in the timer's closure.
11853 unused << copy.forget();
11854 } else {
11855 // If we are frozen, however, then we instead simply set
11856 // timeout->mTimeRemaining to be the "time remaining" in the timeout (i.e.,
11857 // the interval itself). We don't create a timer for it, since that will
11858 // happen when we are thawed and the timeout will then get a timer and run
11859 // to completion.
11861 timeout->mTimeRemaining = delta;
11862 }
11864 timeout->mWindow = this;
11866 if (!aIsInterval) {
11867 timeout->mNestingLevel = nestingLevel;
11868 }
11870 // No popups from timeouts by default
11871 timeout->mPopupState = openAbused;
11873 if (gRunningTimeoutDepth == 0 && gPopupControlState < openAbused) {
11874 // This timeout is *not* set from another timeout and it's set
11875 // while popups are enabled. Propagate the state to the timeout if
11876 // its delay (interval) is equal to or less than what
11877 // "dom.disable_open_click_delay" is set to (in ms).
11879 int32_t delay =
11880 Preferences::GetInt("dom.disable_open_click_delay");
11882 // This is checking |interval|, not realInterval, on purpose,
11883 // because our lower bound for |realInterval| could be pretty high
11884 // in some cases.
11885 if (interval <= delay) {
11886 timeout->mPopupState = gPopupControlState;
11887 }
11888 }
11890 InsertTimeoutIntoList(timeout);
11892 timeout->mPublicId = ++mTimeoutPublicIdCounter;
11893 *aReturn = timeout->mPublicId;
11895 return NS_OK;
11897 }
11899 nsresult
11900 nsGlobalWindow::SetTimeoutOrInterval(bool aIsInterval, int32_t *aReturn)
11901 {
11902 // This needs to forward to the inner window, but since the current
11903 // inner may not be the inner in the calling scope, we need to treat
11904 // this specially here as we don't want timeouts registered in a
11905 // dying inner window to get registered and run on the current inner
11906 // window. To get this right, we need to forward this call to the
11907 // inner window that's calling window.setTimeout().
11909 if (IsOuterWindow()) {
11910 nsGlobalWindow* callerInner = CallerInnerWindow();
11911 NS_ENSURE_TRUE(callerInner, NS_ERROR_NOT_AVAILABLE);
11913 // If the caller and the callee share the same outer window,
11914 // forward to the callee inner. Else, we forward to the current
11915 // inner (e.g. someone is calling setTimeout() on a reference to
11916 // some other window).
11918 if (callerInner->GetOuterWindow() == this &&
11919 callerInner->IsInnerWindow()) {
11920 return callerInner->SetTimeoutOrInterval(aIsInterval, aReturn);
11921 }
11923 FORWARD_TO_INNER(SetTimeoutOrInterval, (aIsInterval, aReturn),
11924 NS_ERROR_NOT_INITIALIZED);
11925 }
11927 int32_t interval = 0;
11928 bool isInterval = aIsInterval;
11929 nsCOMPtr<nsIScriptTimeoutHandler> handler;
11930 nsresult rv = NS_CreateJSTimeoutHandler(this,
11931 &isInterval,
11932 &interval,
11933 getter_AddRefs(handler));
11934 if (!handler) {
11935 *aReturn = 0;
11936 return rv;
11937 }
11939 return SetTimeoutOrInterval(handler, interval, isInterval, aReturn);
11940 }
11942 int32_t
11943 nsGlobalWindow::SetTimeoutOrInterval(Function& aFunction, int32_t aTimeout,
11944 const Sequence<JS::Value>& aArguments,
11945 bool aIsInterval, ErrorResult& aError)
11946 {
11947 nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError);
11948 if (!inner) {
11949 return -1;
11950 }
11952 if (inner != this) {
11953 return inner->SetTimeoutOrInterval(aFunction, aTimeout, aArguments,
11954 aIsInterval, aError);
11955 }
11957 nsCOMPtr<nsIScriptTimeoutHandler> handler =
11958 NS_CreateJSTimeoutHandler(this, aFunction, aArguments, aError);
11959 if (!handler) {
11960 return 0;
11961 }
11963 int32_t result;
11964 aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
11965 return result;
11966 }
11968 int32_t
11969 nsGlobalWindow::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
11970 int32_t aTimeout, bool aIsInterval,
11971 ErrorResult& aError)
11972 {
11973 nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError);
11974 if (!inner) {
11975 return -1;
11976 }
11978 if (inner != this) {
11979 return inner->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
11980 aError);
11981 }
11983 nsCOMPtr<nsIScriptTimeoutHandler> handler =
11984 NS_CreateJSTimeoutHandler(aCx, this, aHandler, aError);
11985 if (!handler) {
11986 return 0;
11987 }
11989 int32_t result;
11990 aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
11991 return result;
11992 }
11994 bool
11995 nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
11996 nsIScriptContext* aScx)
11997 {
11998 // Hold on to the timeout in case mExpr or mFunObj releases its
11999 // doc.
12000 nsRefPtr<nsTimeout> timeout = aTimeout;
12001 nsTimeout* last_running_timeout = mRunningTimeout;
12002 mRunningTimeout = timeout;
12003 timeout->mRunning = true;
12005 // Push this timeout's popup control state, which should only be
12006 // eabled the first time a timeout fires that was created while
12007 // popups were enabled and with a delay less than
12008 // "dom.disable_open_click_delay".
12009 nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState);
12011 // Clear the timeout's popup state, if any, to prevent interval
12012 // timeouts from repeatedly opening poups.
12013 timeout->mPopupState = openAbused;
12015 ++gRunningTimeoutDepth;
12016 ++mTimeoutFiringDepth;
12018 bool trackNestingLevel = !timeout->mIsInterval;
12019 uint32_t nestingLevel;
12020 if (trackNestingLevel) {
12021 nestingLevel = sNestingLevel;
12022 sNestingLevel = timeout->mNestingLevel;
12023 }
12025 nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
12026 nsRefPtr<Function> callback = handler->GetCallback();
12027 if (!callback) {
12028 // Evaluate the timeout expression.
12029 const char16_t* script = handler->GetHandlerText();
12030 NS_ASSERTION(script, "timeout has no script nor handler text!");
12032 const char* filename = nullptr;
12033 uint32_t lineNo = 0;
12034 handler->GetLocation(&filename, &lineNo);
12036 // New script entry point required, due to the "Create a script" sub-step of
12037 // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialization-steps
12038 AutoEntryScript entryScript(this, true, aScx->GetNativeContext());
12039 JS::CompileOptions options(entryScript.cx());
12040 options.setFileAndLine(filename, lineNo)
12041 .setVersion(JSVERSION_DEFAULT);
12042 JS::Rooted<JSObject*> global(entryScript.cx(), FastGetGlobalJSObject());
12043 nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script),
12044 global, options);
12045 } else {
12046 // Hold strong ref to ourselves while we call the callback.
12047 nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
12048 ErrorResult ignored;
12049 JS::Rooted<JS::Value> ignoredVal(CycleCollectedJSRuntime::Get()->Runtime());
12050 callback->Call(me, handler->GetArgs(), &ignoredVal, ignored);
12051 }
12053 // We ignore any failures from calling EvaluateString() on the context or
12054 // Call() on a Function here since we're in a loop
12055 // where we're likely to be running timeouts whose OS timers
12056 // didn't fire in time and we don't want to not fire those timers
12057 // now just because execution of one timer failed. We can't
12058 // propagate the error to anyone who cares about it from this
12059 // point anyway, and the script context should have already reported
12060 // the script error in the usual way - so we just drop it.
12062 if (trackNestingLevel) {
12063 sNestingLevel = nestingLevel;
12064 }
12066 --mTimeoutFiringDepth;
12067 --gRunningTimeoutDepth;
12069 mRunningTimeout = last_running_timeout;
12070 timeout->mRunning = false;
12071 return timeout->mCleared;
12072 }
12074 bool
12075 nsGlobalWindow::RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
12076 bool aRunningPendingTimeouts)
12077 {
12078 if (!aTimeout->mIsInterval) {
12079 if (aTimeout->mTimer) {
12080 // The timeout still has an OS timer, and it's not an interval,
12081 // that means that the OS timer could still fire; cancel the OS
12082 // timer and release its reference to the timeout.
12083 aTimeout->mTimer->Cancel();
12084 aTimeout->mTimer = nullptr;
12085 aTimeout->Release();
12086 }
12087 return false;
12088 }
12090 // Compute time to next timeout for interval timer.
12091 // Make sure nextInterval is at least DOMMinTimeoutValue().
12092 TimeDuration nextInterval =
12093 TimeDuration::FromMilliseconds(std::max(aTimeout->mInterval,
12094 uint32_t(DOMMinTimeoutValue())));
12096 // If we're running pending timeouts, set the next interval to be
12097 // relative to "now", and not to when the timeout that was pending
12098 // should have fired.
12099 TimeStamp firingTime;
12100 if (aRunningPendingTimeouts) {
12101 firingTime = now + nextInterval;
12102 } else {
12103 firingTime = aTimeout->mWhen + nextInterval;
12104 }
12106 TimeStamp currentNow = TimeStamp::Now();
12107 TimeDuration delay = firingTime - currentNow;
12109 // And make sure delay is nonnegative; that might happen if the timer
12110 // thread is firing our timers somewhat early or if they're taking a long
12111 // time to run the callback.
12112 if (delay < TimeDuration(0)) {
12113 delay = TimeDuration(0);
12114 }
12116 if (!aTimeout->mTimer) {
12117 NS_ASSERTION(IsFrozen() || mTimeoutsSuspendDepth,
12118 "How'd our timer end up null if we're not frozen or "
12119 "suspended?");
12121 aTimeout->mTimeRemaining = delay;
12122 return true;
12123 }
12125 aTimeout->mWhen = currentNow + delay;
12127 // Reschedule the OS timer. Don't bother returning any error codes if
12128 // this fails since the callers of this method don't care about them.
12129 nsresult rv = aTimeout->InitTimer(TimerCallback, delay.ToMilliseconds());
12131 if (NS_FAILED(rv)) {
12132 NS_ERROR("Error initializing timer for DOM timeout!");
12134 // We failed to initialize the new OS timer, this timer does
12135 // us no good here so we just cancel it (just in case) and
12136 // null out the pointer to the OS timer, this will release the
12137 // OS timer. As we continue executing the code below we'll end
12138 // up deleting the timeout since it's not an interval timeout
12139 // any more (since timeout->mTimer == nullptr).
12140 aTimeout->mTimer->Cancel();
12141 aTimeout->mTimer = nullptr;
12143 // Now that the OS timer no longer has a reference to the
12144 // timeout we need to drop that reference.
12145 aTimeout->Release();
12147 return false;
12148 }
12150 return true;
12151 }
12153 void
12154 nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
12155 {
12156 // If a modal dialog is open for this window, return early. Pending
12157 // timeouts will run when the modal dialog is dismissed.
12158 if (IsInModalState() || mTimeoutsSuspendDepth) {
12159 return;
12160 }
12162 NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
12163 NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");
12165 nsTimeout *nextTimeout;
12166 nsTimeout *last_expired_timeout, *last_insertion_point;
12167 uint32_t firingDepth = mTimeoutFiringDepth + 1;
12169 // Make sure that the window and the script context don't go away as
12170 // a result of running timeouts
12171 nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(this);
12173 // A native timer has gone off. See which of our timeouts need
12174 // servicing
12175 TimeStamp now = TimeStamp::Now();
12176 TimeStamp deadline;
12178 if (aTimeout && aTimeout->mWhen > now) {
12179 // The OS timer fired early (which can happen due to the timers
12180 // having lower precision than TimeStamp does). Set |deadline| to
12181 // be the time when the OS timer *should* have fired so that any
12182 // timers that *should* have fired before aTimeout *will* be fired
12183 // now.
12185 deadline = aTimeout->mWhen;
12186 } else {
12187 deadline = now;
12188 }
12190 // The timeout list is kept in deadline order. Discover the latest
12191 // timeout whose deadline has expired. On some platforms, native
12192 // timeout events fire "early", so we need to test the timer as well
12193 // as the deadline.
12194 last_expired_timeout = nullptr;
12195 for (nsTimeout *timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
12196 if (((timeout == aTimeout) || (timeout->mWhen <= deadline)) &&
12197 (timeout->mFiringDepth == 0)) {
12198 // Mark any timeouts that are on the list to be fired with the
12199 // firing depth so that we can reentrantly run timeouts
12200 timeout->mFiringDepth = firingDepth;
12201 last_expired_timeout = timeout;
12202 }
12203 }
12205 // Maybe the timeout that the event was fired for has been deleted
12206 // and there are no others timeouts with deadlines that make them
12207 // eligible for execution yet. Go away.
12208 if (!last_expired_timeout) {
12209 return;
12210 }
12212 // Record telemetry information about timers set recently.
12213 TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL);
12214 if (gLastRecordedRecentTimeouts.IsNull() ||
12215 now - gLastRecordedRecentTimeouts > recordingInterval) {
12216 uint32_t count = gTimeoutsRecentlySet;
12217 gTimeoutsRecentlySet = 0;
12218 Telemetry::Accumulate(Telemetry::DOM_TIMERS_RECENTLY_SET, count);
12219 gLastRecordedRecentTimeouts = now;
12220 }
12222 // Insert a dummy timeout into the list of timeouts between the
12223 // portion of the list that we are about to process now and those
12224 // timeouts that will be processed in a future call to
12225 // win_run_timeout(). This dummy timeout serves as the head of the
12226 // list for any timeouts inserted as a result of running a timeout.
12227 nsRefPtr<nsTimeout> dummy_timeout = new nsTimeout();
12228 dummy_timeout->mFiringDepth = firingDepth;
12229 dummy_timeout->mWhen = now;
12230 last_expired_timeout->setNext(dummy_timeout);
12231 dummy_timeout->AddRef();
12233 last_insertion_point = mTimeoutInsertionPoint;
12234 // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
12235 // the logic in ResetTimersForNonBackgroundWindow will need to change.
12236 mTimeoutInsertionPoint = dummy_timeout;
12238 Telemetry::AutoCounter<Telemetry::DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT> timeoutsRan;
12240 for (nsTimeout *timeout = mTimeouts.getFirst();
12241 timeout != dummy_timeout && !IsFrozen();
12242 timeout = nextTimeout) {
12243 nextTimeout = timeout->getNext();
12245 if (timeout->mFiringDepth != firingDepth) {
12246 // We skip the timeout since it's on the list to run at another
12247 // depth.
12249 continue;
12250 }
12252 if (mTimeoutsSuspendDepth) {
12253 // Some timer did suspend us. Make sure the
12254 // rest of the timers get executed later.
12255 timeout->mFiringDepth = 0;
12256 continue;
12257 }
12259 // The timeout is on the list to run at this depth, go ahead and
12260 // process it.
12262 // Get the script context (a strong ref to prevent it going away)
12263 // for this timeout and ensure the script language is enabled.
12264 nsCOMPtr<nsIScriptContext> scx = GetContextInternal();
12266 if (!scx) {
12267 // No context means this window was closed or never properly
12268 // initialized for this language.
12269 continue;
12270 }
12272 // This timeout is good to run
12273 ++timeoutsRan;
12274 bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
12276 if (timeout_was_cleared) {
12277 // The running timeout's window was cleared, this means that
12278 // ClearAllTimeouts() was called from a *nested* call, possibly
12279 // through a timeout that fired while a modal (to this window)
12280 // dialog was open or through other non-obvious paths.
12281 MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
12283 mTimeoutInsertionPoint = last_insertion_point;
12285 return;
12286 }
12288 // If we have a regular interval timer, we re-schedule the
12289 // timeout, accounting for clock drift.
12290 bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
12292 // Running a timeout can cause another timeout to be deleted, so
12293 // we need to reset the pointer to the following timeout.
12294 nextTimeout = timeout->getNext();
12296 timeout->remove();
12298 if (needsReinsertion) {
12299 // Insert interval timeout onto list sorted in deadline order.
12300 // AddRefs timeout.
12301 InsertTimeoutIntoList(timeout);
12302 }
12304 // Release the timeout struct since it's possibly out of the list
12305 timeout->Release();
12306 }
12308 // Take the dummy timeout off the head of the list
12309 dummy_timeout->remove();
12310 dummy_timeout->Release();
12311 MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
12313 mTimeoutInsertionPoint = last_insertion_point;
12314 }
12316 void
12317 nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID, ErrorResult& aError)
12318 {
12319 FORWARD_TO_INNER_OR_THROW(ClearTimeoutOrInterval, (aTimerID, aError),
12320 aError, );
12322 uint32_t public_id = (uint32_t)aTimerID;
12323 nsTimeout *timeout;
12325 for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
12326 if (timeout->mPublicId == public_id) {
12327 if (timeout->mRunning) {
12328 /* We're running from inside the timeout. Mark this
12329 timeout for deferred deletion by the code in
12330 RunTimeout() */
12331 timeout->mIsInterval = false;
12332 }
12333 else {
12334 /* Delete the timeout from the pending timeout list */
12335 timeout->remove();
12337 if (timeout->mTimer) {
12338 timeout->mTimer->Cancel();
12339 timeout->mTimer = nullptr;
12340 timeout->Release();
12341 }
12342 timeout->Release();
12343 }
12344 break;
12345 }
12346 }
12347 }
12349 nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
12350 {
12351 FORWARD_TO_INNER(ResetTimersForNonBackgroundWindow, (),
12352 NS_ERROR_NOT_INITIALIZED);
12354 if (IsFrozen() || mTimeoutsSuspendDepth) {
12355 return NS_OK;
12356 }
12358 TimeStamp now = TimeStamp::Now();
12360 // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
12361 // timers and the timers we're planning to fire all come before
12362 // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
12363 // with an mWhen that may be semi-bogus. In that case, we don't need to do
12364 // anything with mTimeoutInsertionPoint or anything before it, so should
12365 // start at the timer after mTimeoutInsertionPoint, if there is one.
12366 // Otherwise, start at the beginning of the list.
12367 for (nsTimeout *timeout = mTimeoutInsertionPoint ?
12368 mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
12369 timeout; ) {
12370 // It's important that this check be <= so that we guarantee that
12371 // taking std::max with |now| won't make a quantity equal to
12372 // timeout->mWhen below.
12373 if (timeout->mWhen <= now) {
12374 timeout = timeout->getNext();
12375 continue;
12376 }
12378 if (timeout->mWhen - now >
12379 TimeDuration::FromMilliseconds(gMinBackgroundTimeoutValue)) {
12380 // No need to loop further. Timeouts are sorted in mWhen order
12381 // and the ones after this point were all set up for at least
12382 // gMinBackgroundTimeoutValue ms and hence were not clamped.
12383 break;
12384 }
12386 /* We switched from background. Re-init the timer appropriately */
12387 // Compute the interval the timer should have had if it had not been set in a
12388 // background window
12389 TimeDuration interval =
12390 TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
12391 uint32_t(DOMMinTimeoutValue())));
12392 uint32_t oldIntervalMillisecs = 0;
12393 timeout->mTimer->GetDelay(&oldIntervalMillisecs);
12394 TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
12395 if (oldInterval > interval) {
12396 // unclamp
12397 TimeStamp firingTime =
12398 std::max(timeout->mWhen - oldInterval + interval, now);
12400 NS_ASSERTION(firingTime < timeout->mWhen,
12401 "Our firing time should strictly decrease!");
12403 TimeDuration delay = firingTime - now;
12404 timeout->mWhen = firingTime;
12406 // Since we reset mWhen we need to move |timeout| to the right
12407 // place in the list so that it remains sorted by mWhen.
12409 // Get the pointer to the next timeout now, before we move the
12410 // current timeout in the list.
12411 nsTimeout* nextTimeout = timeout->getNext();
12413 // It is safe to remove and re-insert because mWhen is now
12414 // strictly smaller than it used to be, so we know we'll insert
12415 // |timeout| before nextTimeout.
12416 NS_ASSERTION(!nextTimeout ||
12417 timeout->mWhen < nextTimeout->mWhen, "How did that happen?");
12418 timeout->remove();
12419 // InsertTimeoutIntoList will addref |timeout| and reset
12420 // mFiringDepth. Make sure to undo that after calling it.
12421 uint32_t firingDepth = timeout->mFiringDepth;
12422 InsertTimeoutIntoList(timeout);
12423 timeout->mFiringDepth = firingDepth;
12424 timeout->Release();
12426 nsresult rv = timeout->InitTimer(TimerCallback, delay.ToMilliseconds());
12428 if (NS_FAILED(rv)) {
12429 NS_WARNING("Error resetting non background timer for DOM timeout!");
12430 return rv;
12431 }
12433 timeout = nextTimeout;
12434 } else {
12435 timeout = timeout->getNext();
12436 }
12437 }
12439 return NS_OK;
12440 }
12442 void
12443 nsGlobalWindow::ClearAllTimeouts()
12444 {
12445 nsTimeout *timeout, *nextTimeout;
12447 for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
12448 /* If RunTimeout() is higher up on the stack for this
12449 window, e.g. as a result of document.write from a timeout,
12450 then we need to reset the list insertion point for
12451 newly-created timeouts in case the user adds a timeout,
12452 before we pop the stack back to RunTimeout. */
12453 if (mRunningTimeout == timeout)
12454 mTimeoutInsertionPoint = nullptr;
12456 nextTimeout = timeout->getNext();
12458 if (timeout->mTimer) {
12459 timeout->mTimer->Cancel();
12460 timeout->mTimer = nullptr;
12462 // Drop the count since the timer isn't going to hold on
12463 // anymore.
12464 timeout->Release();
12465 }
12467 // Set timeout->mCleared to true to indicate that the timeout was
12468 // cleared and taken out of the list of timeouts
12469 timeout->mCleared = true;
12471 // Drop the count since we're removing it from the list.
12472 timeout->Release();
12473 }
12475 // Clear out our list
12476 mTimeouts.clear();
12477 }
12479 void
12480 nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout)
12481 {
12482 NS_ASSERTION(IsInnerWindow(),
12483 "InsertTimeoutIntoList() called on outer window!");
12485 // Start at mLastTimeout and go backwards. Don't go further than
12486 // mTimeoutInsertionPoint, though. This optimizes for the common case of
12487 // insertion at the end.
12488 nsTimeout* prevSibling;
12489 for (prevSibling = mTimeouts.getLast();
12490 prevSibling && prevSibling != mTimeoutInsertionPoint &&
12491 // This condition needs to match the one in SetTimeoutOrInterval that
12492 // determines whether to set mWhen or mTimeRemaining.
12493 ((IsFrozen() || mTimeoutsSuspendDepth) ?
12494 prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
12495 prevSibling->mWhen > aTimeout->mWhen);
12496 prevSibling = prevSibling->getPrevious()) {
12497 /* Do nothing; just searching */
12498 }
12500 // Now link in aTimeout after prevSibling.
12501 if (prevSibling) {
12502 prevSibling->setNext(aTimeout);
12503 } else {
12504 mTimeouts.insertFront(aTimeout);
12505 }
12507 aTimeout->mFiringDepth = 0;
12509 // Increment the timeout's reference count since it's now held on to
12510 // by the list
12511 aTimeout->AddRef();
12512 }
12514 // static
12515 void
12516 nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure)
12517 {
12518 nsRefPtr<nsTimeout> timeout = (nsTimeout *)aClosure;
12520 timeout->mWindow->RunTimeout(timeout);
12521 }
12523 //*****************************************************************************
12524 // nsGlobalWindow: Helper Functions
12525 //*****************************************************************************
12527 already_AddRefed<nsIDocShellTreeOwner>
12528 nsGlobalWindow::GetTreeOwner()
12529 {
12530 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
12532 // If there's no docShellAsItem, this window must have been closed,
12533 // in that case there is no tree owner.
12535 if (!mDocShell) {
12536 return nullptr;
12537 }
12539 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
12540 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
12541 return treeOwner.forget();
12542 }
12544 already_AddRefed<nsIBaseWindow>
12545 nsGlobalWindow::GetTreeOwnerWindow()
12546 {
12547 MOZ_ASSERT(IsOuterWindow());
12549 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
12551 // If there's no mDocShell, this window must have been closed,
12552 // in that case there is no tree owner.
12554 if (mDocShell) {
12555 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
12556 }
12558 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
12559 return baseWindow.forget();
12560 }
12562 already_AddRefed<nsIWebBrowserChrome>
12563 nsGlobalWindow::GetWebBrowserChrome()
12564 {
12565 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
12567 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
12568 return browserChrome.forget();
12569 }
12571 nsIScrollableFrame *
12572 nsGlobalWindow::GetScrollFrame()
12573 {
12574 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
12576 if (!mDocShell) {
12577 return nullptr;
12578 }
12580 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
12581 if (presShell) {
12582 return presShell->GetRootScrollFrameAsScrollable();
12583 }
12584 return nullptr;
12585 }
12587 nsresult
12588 nsGlobalWindow::SecurityCheckURL(const char *aURL)
12589 {
12590 nsCOMPtr<nsPIDOMWindow> sourceWindow;
12591 JSContext* topCx = nsContentUtils::GetCurrentJSContext();
12592 if (topCx) {
12593 sourceWindow = do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(topCx));
12594 }
12595 if (!sourceWindow) {
12596 sourceWindow = this;
12597 }
12598 AutoJSContext cx;
12599 nsGlobalWindow* sourceWin = static_cast<nsGlobalWindow*>(sourceWindow.get());
12600 JSAutoCompartment ac(cx, sourceWin->GetGlobalJSObject());
12602 // Resolve the baseURI, which could be relative to the calling window.
12603 //
12604 // Note the algorithm to get the base URI should match the one
12605 // used to actually kick off the load in nsWindowWatcher.cpp.
12606 nsCOMPtr<nsIDocument> doc = sourceWindow->GetDoc();
12607 nsIURI* baseURI = nullptr;
12608 nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8
12609 if (doc) {
12610 baseURI = doc->GetDocBaseURI();
12611 charset = doc->GetDocumentCharacterSet();
12612 }
12613 nsCOMPtr<nsIURI> uri;
12614 nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
12615 charset.get(), baseURI);
12616 if (NS_WARN_IF(NS_FAILED(rv))) {
12617 return rv;
12618 }
12620 if (NS_FAILED(nsContentUtils::GetSecurityManager()->
12621 CheckLoadURIFromScript(cx, uri))) {
12622 return NS_ERROR_FAILURE;
12623 }
12625 return NS_OK;
12626 }
12628 void
12629 nsGlobalWindow::FlushPendingNotifications(mozFlushType aType)
12630 {
12631 if (mDoc) {
12632 mDoc->FlushPendingNotifications(aType);
12633 }
12634 }
12636 void
12637 nsGlobalWindow::EnsureSizeUpToDate()
12638 {
12639 MOZ_ASSERT(IsOuterWindow());
12641 // If we're a subframe, make sure our size is up to date. It's OK that this
12642 // crosses the content/chrome boundary, since chrome can have pending reflows
12643 // too.
12644 nsGlobalWindow *parent =
12645 static_cast<nsGlobalWindow *>(GetPrivateParent());
12646 if (parent) {
12647 parent->FlushPendingNotifications(Flush_Layout);
12648 }
12649 }
12651 already_AddRefed<nsISupports>
12652 nsGlobalWindow::SaveWindowState()
12653 {
12654 NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state");
12656 if (!mContext || !GetWrapperPreserveColor()) {
12657 // The window may be getting torn down; don't bother saving state.
12658 return nullptr;
12659 }
12661 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
12662 NS_ASSERTION(inner, "No inner window to save");
12664 // Don't do anything else to this inner window! After this point, all
12665 // calls to SetTimeoutOrInterval will create entries in the timeout
12666 // list that will only run after this window has come out of the bfcache.
12667 // Also, while we're frozen, we won't dispatch online/offline events
12668 // to the page.
12669 inner->Freeze();
12671 nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
12673 #ifdef DEBUG_PAGE_CACHE
12674 printf("saving window state, state = %p\n", (void*)state);
12675 #endif
12677 return state.forget();
12678 }
12680 nsresult
12681 nsGlobalWindow::RestoreWindowState(nsISupports *aState)
12682 {
12683 NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window");
12685 if (!mContext || !GetWrapperPreserveColor()) {
12686 // The window may be getting torn down; don't bother restoring state.
12687 return NS_OK;
12688 }
12690 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
12691 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
12693 #ifdef DEBUG_PAGE_CACHE
12694 printf("restoring window state, state = %p\n", (void*)holder);
12695 #endif
12697 // And we're ready to go!
12698 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
12700 // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes
12701 // it easy to tell which link was last clicked when going back a page.
12702 nsIContent* focusedNode = inner->GetFocusedNode();
12703 if (IsLink(focusedNode)) {
12704 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
12705 if (fm) {
12706 nsCOMPtr<nsIDOMElement> focusedElement(do_QueryInterface(focusedNode));
12707 fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL |
12708 nsIFocusManager::FLAG_SHOWRING);
12709 }
12710 }
12712 inner->Thaw();
12714 holder->DidRestoreWindow();
12716 return NS_OK;
12717 }
12719 void
12720 nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
12721 bool aFreezeChildren)
12722 {
12723 FORWARD_TO_INNER_VOID(SuspendTimeouts, (aIncrease, aFreezeChildren));
12725 bool suspended = (mTimeoutsSuspendDepth != 0);
12726 mTimeoutsSuspendDepth += aIncrease;
12728 if (!suspended) {
12729 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12730 if (ac) {
12731 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
12732 ac->RemoveWindowListener(mEnabledSensors[i], this);
12733 }
12734 DisableGamepadUpdates();
12736 // Suspend all of the workers for this window.
12737 mozilla::dom::workers::SuspendWorkersForWindow(this);
12739 TimeStamp now = TimeStamp::Now();
12740 for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
12741 // Set mTimeRemaining to be the time remaining for this timer.
12742 if (t->mWhen > now)
12743 t->mTimeRemaining = t->mWhen - now;
12744 else
12745 t->mTimeRemaining = TimeDuration(0);
12747 // Drop the XPCOM timer; we'll reschedule when restoring the state.
12748 if (t->mTimer) {
12749 t->mTimer->Cancel();
12750 t->mTimer = nullptr;
12752 // Drop the reference that the timer's closure had on this timeout, we'll
12753 // add it back in ResumeTimeouts. Note that it shouldn't matter that we're
12754 // passing null for the context, since this shouldn't actually release this
12755 // timeout.
12756 t->Release();
12757 }
12758 }
12760 // Suspend all of the AudioContexts for this window
12761 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
12762 mAudioContexts[i]->Suspend();
12763 }
12764 }
12766 // Suspend our children as well.
12767 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
12768 if (docShell) {
12769 int32_t childCount = 0;
12770 docShell->GetChildCount(&childCount);
12772 for (int32_t i = 0; i < childCount; ++i) {
12773 nsCOMPtr<nsIDocShellTreeItem> childShell;
12774 docShell->GetChildAt(i, getter_AddRefs(childShell));
12775 NS_ASSERTION(childShell, "null child shell");
12777 nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell);
12778 if (pWin) {
12779 nsGlobalWindow *win =
12780 static_cast<nsGlobalWindow*>
12781 (static_cast<nsPIDOMWindow*>(pWin));
12782 NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
12783 nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
12785 // This is a bit hackish. Only freeze/suspend windows which are truly our
12786 // subwindows.
12787 nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
12788 if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
12789 continue;
12790 }
12792 win->SuspendTimeouts(aIncrease, aFreezeChildren);
12794 if (inner && aFreezeChildren) {
12795 inner->Freeze();
12796 }
12797 }
12798 }
12799 }
12800 }
12802 nsresult
12803 nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
12804 {
12805 FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED);
12807 NS_ASSERTION(mTimeoutsSuspendDepth, "Mismatched calls to ResumeTimeouts!");
12808 --mTimeoutsSuspendDepth;
12809 bool shouldResume = (mTimeoutsSuspendDepth == 0) && !mInnerObjectsFreed;
12810 nsresult rv;
12812 if (shouldResume) {
12813 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12814 if (ac) {
12815 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
12816 ac->AddWindowListener(mEnabledSensors[i], this);
12817 }
12818 EnableGamepadUpdates();
12820 // Resume all of the AudioContexts for this window
12821 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
12822 mAudioContexts[i]->Resume();
12823 }
12825 // Resume all of the workers for this window.
12826 mozilla::dom::workers::ResumeWorkersForWindow(this);
12828 // Restore all of the timeouts, using the stored time remaining
12829 // (stored in timeout->mTimeRemaining).
12831 TimeStamp now = TimeStamp::Now();
12833 #ifdef DEBUG
12834 bool _seenDummyTimeout = false;
12835 #endif
12837 for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
12838 // There's a chance we're being called with RunTimeout on the stack in which
12839 // case we have a dummy timeout in the list that *must not* be resumed. It
12840 // can be identified by a null mWindow.
12841 if (!t->mWindow) {
12842 #ifdef DEBUG
12843 NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
12844 _seenDummyTimeout = true;
12845 #endif
12846 continue;
12847 }
12849 // XXXbz the combination of the way |delay| and |t->mWhen| are set here
12850 // makes no sense. Are we trying to impose that min timeout value or
12851 // not???
12852 uint32_t delay =
12853 std::max(int32_t(t->mTimeRemaining.ToMilliseconds()),
12854 DOMMinTimeoutValue());
12856 // Set mWhen back to the time when the timer is supposed to
12857 // fire.
12858 t->mWhen = now + t->mTimeRemaining;
12860 t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
12861 NS_ENSURE_TRUE(t->mTimer, NS_ERROR_OUT_OF_MEMORY);
12863 rv = t->InitTimer(TimerCallback, delay);
12864 if (NS_FAILED(rv)) {
12865 t->mTimer = nullptr;
12866 return rv;
12867 }
12869 // Add a reference for the new timer's closure.
12870 t->AddRef();
12871 }
12872 }
12874 // Resume our children as well.
12875 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
12876 if (docShell) {
12877 int32_t childCount = 0;
12878 docShell->GetChildCount(&childCount);
12880 for (int32_t i = 0; i < childCount; ++i) {
12881 nsCOMPtr<nsIDocShellTreeItem> childShell;
12882 docShell->GetChildAt(i, getter_AddRefs(childShell));
12883 NS_ASSERTION(childShell, "null child shell");
12885 nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell);
12886 if (pWin) {
12887 nsGlobalWindow *win =
12888 static_cast<nsGlobalWindow*>
12889 (static_cast<nsPIDOMWindow*>(pWin));
12891 NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
12892 nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
12894 // This is a bit hackish. Only thaw/resume windows which are truly our
12895 // subwindows.
12896 nsCOMPtr<Element> frame = pWin->GetFrameElementInternal();
12897 if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) {
12898 continue;
12899 }
12901 if (inner && aThawChildren) {
12902 inner->Thaw();
12903 }
12905 rv = win->ResumeTimeouts(aThawChildren);
12906 NS_ENSURE_SUCCESS(rv, rv);
12907 }
12908 }
12909 }
12911 return NS_OK;
12912 }
12914 uint32_t
12915 nsGlobalWindow::TimeoutSuspendCount()
12916 {
12917 FORWARD_TO_INNER(TimeoutSuspendCount, (), 0);
12918 return mTimeoutsSuspendDepth;
12919 }
12921 void
12922 nsGlobalWindow::EnableDeviceSensor(uint32_t aType)
12923 {
12924 MOZ_ASSERT(IsInnerWindow());
12926 bool alreadyEnabled = false;
12927 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
12928 if (mEnabledSensors[i] == aType) {
12929 alreadyEnabled = true;
12930 break;
12931 }
12932 }
12934 mEnabledSensors.AppendElement(aType);
12936 if (alreadyEnabled) {
12937 return;
12938 }
12940 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12941 if (ac) {
12942 ac->AddWindowListener(aType, this);
12943 }
12944 }
12946 void
12947 nsGlobalWindow::DisableDeviceSensor(uint32_t aType)
12948 {
12949 MOZ_ASSERT(IsInnerWindow());
12951 int32_t doomedElement = -1;
12952 int32_t listenerCount = 0;
12953 for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) {
12954 if (mEnabledSensors[i] == aType) {
12955 doomedElement = i;
12956 listenerCount++;
12957 }
12958 }
12960 if (doomedElement == -1) {
12961 return;
12962 }
12964 mEnabledSensors.RemoveElementAt(doomedElement);
12966 if (listenerCount > 1) {
12967 return;
12968 }
12970 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12971 if (ac) {
12972 ac->RemoveWindowListener(aType, this);
12973 }
12974 }
12976 void
12977 nsGlobalWindow::SetHasGamepadEventListener(bool aHasGamepad/* = true*/)
12978 {
12979 FORWARD_TO_INNER_VOID(SetHasGamepadEventListener, (aHasGamepad));
12980 mHasGamepad = aHasGamepad;
12981 if (aHasGamepad) {
12982 EnableGamepadUpdates();
12983 }
12984 }
12986 void
12987 nsGlobalWindow::EnableTimeChangeNotifications()
12988 {
12989 mozilla::time::AddWindowListener(this);
12990 }
12992 void
12993 nsGlobalWindow::DisableTimeChangeNotifications()
12994 {
12995 mozilla::time::RemoveWindowListener(this);
12996 }
12998 static PLDHashOperator
12999 CollectSizeAndListenerCount(
13000 nsPtrHashKey<DOMEventTargetHelper>* aEntry,
13001 void *arg)
13002 {
13003 nsWindowSizes* windowSizes = static_cast<nsWindowSizes*>(arg);
13005 DOMEventTargetHelper* et = aEntry->GetKey();
13007 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
13008 windowSizes->mDOMEventTargetsSize +=
13009 iSizeOf->SizeOfEventTargetIncludingThis(windowSizes->mMallocSizeOf);
13010 }
13012 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
13013 windowSizes->mDOMEventListenersCount += elm->ListenerCount();
13014 }
13016 return PL_DHASH_NEXT;
13017 }
13019 void
13020 nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
13021 {
13022 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
13024 if (IsInnerWindow()) {
13025 EventListenerManager* elm = GetExistingListenerManager();
13026 if (elm) {
13027 aWindowSizes->mDOMOtherSize +=
13028 elm->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
13029 aWindowSizes->mDOMEventListenersCount +=
13030 elm->ListenerCount();
13031 }
13032 if (mDoc) {
13033 mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
13034 }
13035 }
13037 if (mNavigator) {
13038 aWindowSizes->mDOMOtherSize +=
13039 mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
13040 }
13042 // The things pointed to by the entries will be measured below, so we
13043 // use nullptr for the callback here.
13044 aWindowSizes->mDOMEventTargetsSize +=
13045 mEventTargetObjects.SizeOfExcludingThis(nullptr,
13046 aWindowSizes->mMallocSizeOf);
13047 aWindowSizes->mDOMEventTargetsCount +=
13048 const_cast<nsTHashtable<nsPtrHashKey<DOMEventTargetHelper> >*>
13049 (&mEventTargetObjects)->EnumerateEntries(CollectSizeAndListenerCount,
13050 aWindowSizes);
13051 }
13054 #ifdef MOZ_GAMEPAD
13055 void
13056 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
13057 {
13058 FORWARD_TO_INNER_VOID(AddGamepad, (aIndex, aGamepad));
13059 mGamepads.Put(aIndex, aGamepad);
13060 }
13062 void
13063 nsGlobalWindow::RemoveGamepad(uint32_t aIndex)
13064 {
13065 FORWARD_TO_INNER_VOID(RemoveGamepad, (aIndex));
13066 mGamepads.Remove(aIndex);
13067 }
13069 // static
13070 PLDHashOperator
13071 nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData,
13072 void* aUserArg)
13073 {
13074 nsTArray<nsRefPtr<Gamepad> >* array =
13075 static_cast<nsTArray<nsRefPtr<Gamepad> >*>(aUserArg);
13076 array->EnsureLengthAtLeast(aKey + 1);
13077 (*array)[aKey] = aData;
13078 return PL_DHASH_NEXT;
13079 }
13081 void
13082 nsGlobalWindow::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads)
13083 {
13084 FORWARD_TO_INNER_VOID(GetGamepads, (aGamepads));
13085 aGamepads.Clear();
13086 // mGamepads.Count() may not be sufficient, but it's not harmful.
13087 aGamepads.SetCapacity(mGamepads.Count());
13088 mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads);
13089 }
13091 already_AddRefed<Gamepad>
13092 nsGlobalWindow::GetGamepad(uint32_t aIndex)
13093 {
13094 FORWARD_TO_INNER(GetGamepad, (aIndex), nullptr);
13095 nsRefPtr<Gamepad> gamepad;
13096 if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
13097 return gamepad.forget();
13098 }
13100 return nullptr;
13101 }
13103 void
13104 nsGlobalWindow::SetHasSeenGamepadInput(bool aHasSeen)
13105 {
13106 FORWARD_TO_INNER_VOID(SetHasSeenGamepadInput, (aHasSeen));
13107 mHasSeenGamepadInput = aHasSeen;
13108 }
13110 bool
13111 nsGlobalWindow::HasSeenGamepadInput()
13112 {
13113 FORWARD_TO_INNER(HasSeenGamepadInput, (), false);
13114 return mHasSeenGamepadInput;
13115 }
13117 // static
13118 PLDHashOperator
13119 nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, Gamepad* aData,
13120 void* aUserArg)
13121 {
13122 nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
13123 gamepadsvc->SyncGamepadState(aKey, aData);
13124 return PL_DHASH_NEXT;
13125 }
13127 void
13128 nsGlobalWindow::SyncGamepadState()
13129 {
13130 FORWARD_TO_INNER_VOID(SyncGamepadState, ());
13131 if (mHasSeenGamepadInput) {
13132 mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr);
13133 }
13134 }
13135 #endif
13136 // nsGlobalChromeWindow implementation
13138 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
13140 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
13141 nsGlobalWindow)
13142 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
13143 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
13144 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
13147 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow,
13148 nsGlobalWindow)
13149 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserDOMWindow)
13150 if (tmp->mMessageManager) {
13151 static_cast<nsFrameMessageManager*>(
13152 tmp->mMessageManager.get())->Disconnect();
13153 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
13154 }
13155 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
13157 DOMCI_DATA(ChromeWindow, nsGlobalChromeWindow)
13159 // QueryInterface implementation for nsGlobalChromeWindow
13160 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalChromeWindow)
13161 NS_INTERFACE_MAP_ENTRY(nsIDOMChromeWindow)
13162 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ChromeWindow)
13163 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
13165 NS_IMPL_ADDREF_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
13166 NS_IMPL_RELEASE_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
13168 NS_IMETHODIMP
13169 nsGlobalChromeWindow::GetWindowState(uint16_t* aWindowState)
13170 {
13171 *aWindowState = WindowState();
13172 return NS_OK;
13173 }
13175 uint16_t
13176 nsGlobalWindow::WindowState()
13177 {
13178 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13180 int32_t mode = widget ? widget->SizeMode() : 0;
13182 switch (mode) {
13183 case nsSizeMode_Minimized:
13184 return nsIDOMChromeWindow::STATE_MINIMIZED;
13185 case nsSizeMode_Maximized:
13186 return nsIDOMChromeWindow::STATE_MAXIMIZED;
13187 case nsSizeMode_Fullscreen:
13188 return nsIDOMChromeWindow::STATE_FULLSCREEN;
13189 case nsSizeMode_Normal:
13190 return nsIDOMChromeWindow::STATE_NORMAL;
13191 default:
13192 NS_WARNING("Illegal window state for this chrome window");
13193 break;
13194 }
13196 return nsIDOMChromeWindow::STATE_NORMAL;
13197 }
13199 NS_IMETHODIMP
13200 nsGlobalChromeWindow::Maximize()
13201 {
13202 ErrorResult rv;
13203 Maximize(rv);
13204 return rv.ErrorCode();
13205 }
13207 void
13208 nsGlobalWindow::Maximize(ErrorResult& aError)
13209 {
13210 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13212 if (widget) {
13213 aError = widget->SetSizeMode(nsSizeMode_Maximized);
13214 }
13215 }
13217 NS_IMETHODIMP
13218 nsGlobalChromeWindow::Minimize()
13219 {
13220 ErrorResult rv;
13221 Minimize(rv);
13222 return rv.ErrorCode();
13223 }
13225 void
13226 nsGlobalWindow::Minimize(ErrorResult& aError)
13227 {
13228 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13230 if (widget) {
13231 aError = widget->SetSizeMode(nsSizeMode_Minimized);
13232 }
13233 }
13235 NS_IMETHODIMP
13236 nsGlobalChromeWindow::Restore()
13237 {
13238 ErrorResult rv;
13239 Restore(rv);
13240 return rv.ErrorCode();
13241 }
13243 void
13244 nsGlobalWindow::Restore(ErrorResult& aError)
13245 {
13246 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13248 if (widget) {
13249 aError = widget->SetSizeMode(nsSizeMode_Normal);
13250 }
13251 }
13253 NS_IMETHODIMP
13254 nsGlobalChromeWindow::GetAttention()
13255 {
13256 ErrorResult rv;
13257 GetAttention(rv);
13258 return rv.ErrorCode();
13259 }
13261 void
13262 nsGlobalWindow::GetAttention(ErrorResult& aResult)
13263 {
13264 return GetAttentionWithCycleCount(-1, aResult);
13265 }
13267 NS_IMETHODIMP
13268 nsGlobalChromeWindow::GetAttentionWithCycleCount(int32_t aCycleCount)
13269 {
13270 ErrorResult rv;
13271 GetAttentionWithCycleCount(aCycleCount, rv);
13272 return rv.ErrorCode();
13273 }
13275 void
13276 nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount,
13277 ErrorResult& aError)
13278 {
13279 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13281 if (widget) {
13282 aError = widget->GetAttention(aCycleCount);
13283 }
13284 }
13286 NS_IMETHODIMP
13287 nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent, nsIDOMElement* aPanel)
13288 {
13289 NS_ENSURE_TRUE(aMouseDownEvent, NS_ERROR_FAILURE);
13290 Event* mouseDownEvent = aMouseDownEvent->InternalDOMEvent();
13291 NS_ENSURE_TRUE(mouseDownEvent, NS_ERROR_FAILURE);
13293 nsCOMPtr<Element> panel = do_QueryInterface(aPanel);
13294 NS_ENSURE_TRUE(panel || !aPanel, NS_ERROR_FAILURE);
13296 ErrorResult rv;
13297 BeginWindowMove(*mouseDownEvent, panel, rv);
13298 return rv.ErrorCode();
13299 }
13301 void
13302 nsGlobalWindow::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel,
13303 ErrorResult& aError)
13304 {
13305 nsCOMPtr<nsIWidget> widget;
13307 // if a panel was supplied, use its widget instead.
13308 #ifdef MOZ_XUL
13309 if (aPanel) {
13310 nsIFrame* frame = aPanel->GetPrimaryFrame();
13311 if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame) {
13312 return;
13313 }
13315 widget = (static_cast<nsMenuPopupFrame*>(frame))->GetWidget();
13316 }
13317 else {
13318 #endif
13319 widget = GetMainWidget();
13320 #ifdef MOZ_XUL
13321 }
13322 #endif
13324 if (!widget) {
13325 return;
13326 }
13328 WidgetMouseEvent* mouseEvent =
13329 aMouseDownEvent.GetInternalNSEvent()->AsMouseEvent();
13330 if (!mouseEvent || mouseEvent->eventStructType != NS_MOUSE_EVENT) {
13331 aError.Throw(NS_ERROR_FAILURE);
13332 return;
13333 }
13335 aError = widget->BeginMoveDrag(mouseEvent);
13336 }
13338 //Note: This call will lock the cursor, it will not change as it moves.
13339 //To unlock, the cursor must be set back to CURSOR_AUTO.
13340 NS_IMETHODIMP
13341 nsGlobalChromeWindow::SetCursor(const nsAString& aCursor)
13342 {
13343 ErrorResult rv;
13344 SetCursor(aCursor, rv);
13345 return rv.ErrorCode();
13346 }
13348 void
13349 nsGlobalWindow::SetCursor(const nsAString& aCursor, ErrorResult& aError)
13350 {
13351 FORWARD_TO_OUTER_OR_THROW(SetCursor, (aCursor, aError), aError, );
13353 int32_t cursor;
13355 if (aCursor.EqualsLiteral("auto"))
13356 cursor = NS_STYLE_CURSOR_AUTO;
13357 else {
13358 nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aCursor);
13359 if (eCSSKeyword_UNKNOWN == keyword ||
13360 !nsCSSProps::FindKeyword(keyword, nsCSSProps::kCursorKTable, cursor)) {
13361 return;
13362 }
13363 }
13365 nsRefPtr<nsPresContext> presContext;
13366 if (mDocShell) {
13367 mDocShell->GetPresContext(getter_AddRefs(presContext));
13368 }
13370 if (presContext) {
13371 // Need root widget.
13372 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
13373 if (!presShell) {
13374 aError.Throw(NS_ERROR_FAILURE);
13375 return;
13376 }
13378 nsViewManager* vm = presShell->GetViewManager();
13379 if (!vm) {
13380 aError.Throw(NS_ERROR_FAILURE);
13381 return;
13382 }
13384 nsView* rootView = vm->GetRootView();
13385 if (!rootView) {
13386 aError.Throw(NS_ERROR_FAILURE);
13387 return;
13388 }
13390 nsIWidget* widget = rootView->GetNearestWidget(nullptr);
13391 if (!widget) {
13392 aError.Throw(NS_ERROR_FAILURE);
13393 return;
13394 }
13396 // Call esm and set cursor.
13397 aError = presContext->EventStateManager()->SetCursor(cursor, nullptr,
13398 false, 0.0f, 0.0f,
13399 widget, true);
13400 }
13401 }
13403 NS_IMETHODIMP
13404 nsGlobalChromeWindow::GetBrowserDOMWindow(nsIBrowserDOMWindow **aBrowserWindow)
13405 {
13406 ErrorResult rv;
13407 NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv));
13408 return rv.ErrorCode();
13409 }
13411 nsIBrowserDOMWindow*
13412 nsGlobalWindow::GetBrowserDOMWindow(ErrorResult& aError)
13413 {
13414 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (aError), aError, nullptr);
13416 MOZ_ASSERT(IsChromeWindow());
13417 return static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow;
13418 }
13420 NS_IMETHODIMP
13421 nsGlobalChromeWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow *aBrowserWindow)
13422 {
13423 ErrorResult rv;
13424 SetBrowserDOMWindow(aBrowserWindow, rv);
13425 return rv.ErrorCode();
13426 }
13428 void
13429 nsGlobalWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow,
13430 ErrorResult& aError)
13431 {
13432 FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindow, (aBrowserWindow, aError),
13433 aError, );
13434 MOZ_ASSERT(IsChromeWindow());
13435 static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow = aBrowserWindow;
13436 }
13438 NS_IMETHODIMP
13439 nsGlobalChromeWindow::NotifyDefaultButtonLoaded(nsIDOMElement* aDefaultButton)
13440 {
13441 nsCOMPtr<Element> defaultButton = do_QueryInterface(aDefaultButton);
13442 NS_ENSURE_ARG(defaultButton);
13444 ErrorResult rv;
13445 NotifyDefaultButtonLoaded(*defaultButton, rv);
13446 return rv.ErrorCode();
13447 }
13449 void
13450 nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton,
13451 ErrorResult& aError)
13452 {
13453 #ifdef MOZ_XUL
13454 // Don't snap to a disabled button.
13455 nsCOMPtr<nsIDOMXULControlElement> xulControl =
13456 do_QueryInterface(&aDefaultButton);
13457 if (!xulControl) {
13458 aError.Throw(NS_ERROR_FAILURE);
13459 return;
13460 }
13461 bool disabled;
13462 aError = xulControl->GetDisabled(&disabled);
13463 if (aError.Failed() || disabled) {
13464 return;
13465 }
13467 // Get the button rect in screen coordinates.
13468 nsIFrame *frame = aDefaultButton.GetPrimaryFrame();
13469 if (!frame) {
13470 aError.Throw(NS_ERROR_FAILURE);
13471 return;
13472 }
13473 nsIntRect buttonRect = frame->GetScreenRect();
13475 // Get the widget rect in screen coordinates.
13476 nsIWidget *widget = GetNearestWidget();
13477 if (!widget) {
13478 aError.Throw(NS_ERROR_FAILURE);
13479 return;
13480 }
13481 nsIntRect widgetRect;
13482 aError = widget->GetScreenBounds(widgetRect);
13483 if (aError.Failed()) {
13484 return;
13485 }
13487 // Convert the buttonRect coordinates from screen to the widget.
13488 buttonRect -= widgetRect.TopLeft();
13489 nsresult rv = widget->OnDefaultButtonLoaded(buttonRect);
13490 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
13491 aError.Throw(rv);
13492 }
13493 #else
13494 aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
13495 #endif
13496 }
13498 NS_IMETHODIMP
13499 nsGlobalChromeWindow::GetMessageManager(nsIMessageBroadcaster** aManager)
13500 {
13501 ErrorResult rv;
13502 NS_IF_ADDREF(*aManager = GetMessageManager(rv));
13503 return rv.ErrorCode();
13504 }
13506 nsIMessageBroadcaster*
13507 nsGlobalWindow::GetMessageManager(ErrorResult& aError)
13508 {
13509 FORWARD_TO_INNER_OR_THROW(GetMessageManager, (aError), aError, nullptr);
13510 MOZ_ASSERT(IsChromeWindow());
13511 nsGlobalChromeWindow* myself = static_cast<nsGlobalChromeWindow*>(this);
13512 if (!myself->mMessageManager) {
13513 nsIScriptContext* scx = GetContextInternal();
13514 if (NS_WARN_IF(!scx || !(scx->GetNativeContext()))) {
13515 aError.Throw(NS_ERROR_UNEXPECTED);
13516 return nullptr;
13517 }
13519 nsCOMPtr<nsIMessageBroadcaster> globalMM =
13520 do_GetService("@mozilla.org/globalmessagemanager;1");
13521 myself->mMessageManager =
13522 new nsFrameMessageManager(nullptr,
13523 static_cast<nsFrameMessageManager*>(globalMM.get()),
13524 MM_CHROME | MM_BROADCASTER);
13525 }
13526 return myself->mMessageManager;
13527 }
13529 // nsGlobalModalWindow implementation
13531 // QueryInterface implementation for nsGlobalModalWindow
13532 DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow)
13534 NS_INTERFACE_MAP_BEGIN(nsGlobalModalWindow)
13535 NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow)
13536 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow)
13537 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
13539 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
13540 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
13543 void
13544 nsGlobalWindow::GetDialogArguments(JSContext* aCx,
13545 JS::MutableHandle<JS::Value> aRetval,
13546 ErrorResult& aError)
13547 {
13548 FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aRetval, aError),
13549 aError, );
13551 MOZ_ASSERT(IsModalContentWindow(),
13552 "This should only be called on modal windows!");
13554 // This does an internal origin check, and returns undefined if the subject
13555 // does not subsumes the origin of the arguments.
13556 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
13557 JSAutoCompartment ac(aCx, wrapper);
13558 mDialogArguments->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(),
13559 aRetval, aError);
13560 }
13562 NS_IMETHODIMP
13563 nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
13564 {
13565 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
13566 NS_ERROR_NOT_INITIALIZED);
13568 // This does an internal origin check, and returns undefined if the subject
13569 // does not subsumes the origin of the arguments.
13570 return mDialogArguments->Get(nsContentUtils::GetSubjectPrincipal(), aArguments);
13571 }
13573 void
13574 nsGlobalWindow::GetReturnValue(JSContext* aCx,
13575 JS::MutableHandle<JS::Value> aReturnValue,
13576 ErrorResult& aError)
13577 {
13578 FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aReturnValue, aError),
13579 aError, );
13581 MOZ_ASSERT(IsModalContentWindow(),
13582 "This should only be called on modal windows!");
13584 if (mReturnValue) {
13585 JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
13586 JSAutoCompartment ac(aCx, wrapper);
13587 mReturnValue->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(),
13588 aReturnValue, aError);
13589 } else {
13590 aReturnValue.setUndefined();
13591 }
13592 }
13594 NS_IMETHODIMP
13595 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
13596 {
13597 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
13599 nsCOMPtr<nsIVariant> result;
13600 if (!mReturnValue) {
13601 nsCOMPtr<nsIVariant> variant = CreateVoidVariant();
13602 variant.forget(aRetVal);
13603 return NS_OK;
13604 }
13605 return mReturnValue->Get(nsContentUtils::GetSubjectPrincipal(), aRetVal);
13606 }
13608 void
13609 nsGlobalWindow::SetReturnValue(JSContext* aCx,
13610 JS::Handle<JS::Value> aReturnValue,
13611 ErrorResult& aError)
13612 {
13613 FORWARD_TO_OUTER_OR_THROW(SetReturnValue, (aCx, aReturnValue, aError),
13614 aError, );
13616 MOZ_ASSERT(IsModalContentWindow(),
13617 "This should only be called on modal windows!");
13619 nsCOMPtr<nsIVariant> returnValue;
13620 aError =
13621 nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue,
13622 getter_AddRefs(returnValue));
13623 if (!aError.Failed()) {
13624 mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(),
13625 returnValue);
13626 }
13627 }
13629 NS_IMETHODIMP
13630 nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal)
13631 {
13632 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK);
13634 mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(),
13635 aRetVal);
13636 return NS_OK;
13637 }
13639 /* static */
13640 bool
13641 nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal)
13642 {
13643 // For now, have to deal with XPConnect objects here.
13644 return xpc::WindowOrNull(aGlobal)->IsModalContentWindow();
13645 }
13647 NS_IMETHODIMP
13648 nsGlobalWindow::GetConsole(JSContext* aCx,
13649 JS::MutableHandle<JS::Value> aConsole)
13650 {
13651 ErrorResult rv;
13652 nsRefPtr<Console> console = GetConsole(rv);
13653 if (rv.Failed()) {
13654 return rv.ErrorCode();
13655 }
13657 if (!WrapNewBindingObject(aCx, console, aConsole)) {
13658 return NS_ERROR_FAILURE;
13659 }
13661 return NS_OK;
13662 }
13664 NS_IMETHODIMP
13665 nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
13666 {
13667 JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
13668 if (!thisObj) {
13669 return NS_ERROR_UNEXPECTED;
13670 }
13672 if (!JS_WrapObject(aCx, &thisObj) ||
13673 !JS_DefineProperty(aCx, thisObj, "console", aValue,
13674 JSPROP_ENUMERATE, JS_PropertyStub,
13675 JS_StrictPropertyStub)) {
13676 return NS_ERROR_FAILURE;
13677 }
13679 return NS_OK;
13680 }
13682 Console*
13683 nsGlobalWindow::GetConsole(ErrorResult& aRv)
13684 {
13685 FORWARD_TO_INNER_OR_THROW(GetConsole, (aRv), aRv, nullptr);
13687 if (!mConsole) {
13688 mConsole = new Console(this);
13689 }
13691 return mConsole;
13692 }
13694 already_AddRefed<External>
13695 nsGlobalWindow::GetExternal(ErrorResult& aRv)
13696 {
13697 FORWARD_TO_INNER_OR_THROW(GetExternal, (aRv), aRv, nullptr);
13699 #ifdef HAVE_SIDEBAR
13700 if (!mExternal) {
13701 AutoJSContext cx;
13702 JS::Rooted<JSObject*> jsImplObj(cx);
13703 ConstructJSImplementation(cx, "@mozilla.org/sidebar;1",
13704 this, &jsImplObj, aRv);
13705 if (aRv.Failed()) {
13706 return nullptr;
13707 }
13708 mExternal = new External(jsImplObj, this);
13709 }
13711 nsRefPtr<External> external = static_cast<External*>(mExternal.get());
13712 return external.forget();
13713 #else
13714 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
13715 return nullptr;
13716 #endif
13717 }
13719 void
13720 nsGlobalWindow::GetSidebar(OwningExternalOrWindowProxy& aResult,
13721 ErrorResult& aRv)
13722 {
13723 FORWARD_TO_INNER_OR_THROW(GetSidebar, (aResult, aRv), aRv, );
13725 #ifdef HAVE_SIDEBAR
13726 // First check for a named frame named "sidebar"
13727 nsCOMPtr<nsIDOMWindow> domWindow = GetChildWindow(NS_LITERAL_STRING("sidebar"));
13728 if (domWindow) {
13729 aResult.SetAsWindowProxy() = domWindow.forget();
13730 return;
13731 }
13733 nsRefPtr<External> external = GetExternal(aRv);
13734 if (external) {
13735 aResult.SetAsExternal() = external;
13736 }
13737 #else
13738 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
13739 #endif
13740 }
13742 /* static */
13743 bool
13744 nsGlobalWindow::WindowOnWebIDL(JSContext* aCx, JSObject* aObj)
13745 {
13746 DebugOnly<nsGlobalWindow*> win;
13747 MOZ_ASSERT_IF(IsDOMObject(aObj),
13748 NS_SUCCEEDED(UNWRAP_OBJECT(Window, aObj, win)));
13750 return IsDOMObject(aObj);
13751 }
13753 #ifdef MOZ_B2G
13754 void
13755 nsGlobalWindow::EnableNetworkEvent(uint32_t aType)
13756 {
13757 MOZ_ASSERT(IsInnerWindow());
13759 nsCOMPtr<nsIPermissionManager> permMgr =
13760 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
13761 if (!permMgr) {
13762 NS_ERROR("No PermissionManager available!");
13763 return;
13764 }
13766 uint32_t permission = nsIPermissionManager::DENY_ACTION;
13767 permMgr->TestExactPermissionFromPrincipal(GetPrincipal(), "network-events",
13768 &permission);
13770 if (permission != nsIPermissionManager::ALLOW_ACTION) {
13771 return;
13772 }
13774 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
13775 if (!os) {
13776 NS_ERROR("ObserverService should be available!");
13777 return;
13778 }
13780 switch (aType) {
13781 case NS_NETWORK_UPLOAD_EVENT:
13782 if (!mNetworkUploadObserverEnabled) {
13783 mNetworkUploadObserverEnabled = true;
13784 os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC, false);
13785 }
13786 break;
13787 case NS_NETWORK_DOWNLOAD_EVENT:
13788 if (!mNetworkDownloadObserverEnabled) {
13789 mNetworkDownloadObserverEnabled = true;
13790 os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, false);
13791 }
13792 break;
13793 }
13794 }
13796 void
13797 nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
13798 {
13799 MOZ_ASSERT(IsInnerWindow());
13801 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
13802 if (!os) {
13803 return;
13804 }
13806 switch (aType) {
13807 case NS_NETWORK_UPLOAD_EVENT:
13808 if (mNetworkUploadObserverEnabled) {
13809 mNetworkUploadObserverEnabled = false;
13810 os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC);
13811 }
13812 break;
13813 case NS_NETWORK_DOWNLOAD_EVENT:
13814 if (mNetworkDownloadObserverEnabled) {
13815 mNetworkDownloadObserverEnabled = false;
13816 os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC);
13817 }
13818 break;
13819 }
13820 }
13821 #endif // MOZ_B2G
13823 #define EVENT(name_, id_, type_, struct_) \
13824 NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \
13825 JS::MutableHandle<JS::Value> vp) { \
13826 EventHandlerNonNull* h = GetOn##name_(); \
13827 vp.setObjectOrNull(h ? h->Callable().get() : nullptr); \
13828 return NS_OK; \
13829 } \
13830 NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \
13831 JS::Handle<JS::Value> v) { \
13832 nsRefPtr<EventHandlerNonNull> handler; \
13833 JS::Rooted<JSObject*> callable(cx); \
13834 if (v.isObject() && \
13835 JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
13836 handler = new EventHandlerNonNull(callable, GetIncumbentGlobal()); \
13837 } \
13838 SetOn##name_(handler); \
13839 return NS_OK; \
13840 }
13841 #define ERROR_EVENT(name_, id_, type_, struct_) \
13842 NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \
13843 JS::MutableHandle<JS::Value> vp) { \
13844 EventListenerManager *elm = GetExistingListenerManager(); \
13845 if (elm) { \
13846 OnErrorEventHandlerNonNull* h = elm->GetOnErrorEventHandler(); \
13847 if (h) { \
13848 vp.setObject(*h->Callable()); \
13849 return NS_OK; \
13850 } \
13851 } \
13852 vp.setNull(); \
13853 return NS_OK; \
13854 } \
13855 NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \
13856 JS::Handle<JS::Value> v) { \
13857 EventListenerManager *elm = GetOrCreateListenerManager(); \
13858 if (!elm) { \
13859 return NS_ERROR_OUT_OF_MEMORY; \
13860 } \
13861 \
13862 nsRefPtr<OnErrorEventHandlerNonNull> handler; \
13863 JS::Rooted<JSObject*> callable(cx); \
13864 if (v.isObject() && \
13865 JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
13866 handler = new OnErrorEventHandlerNonNull(callable, GetIncumbentGlobal()); \
13867 } \
13868 elm->SetEventHandler(handler); \
13869 return NS_OK; \
13870 }
13871 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \
13872 NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \
13873 JS::MutableHandle<JS::Value> vp) { \
13874 EventListenerManager *elm = GetExistingListenerManager(); \
13875 if (elm) { \
13876 OnBeforeUnloadEventHandlerNonNull* h = \
13877 elm->GetOnBeforeUnloadEventHandler(); \
13878 if (h) { \
13879 vp.setObject(*h->Callable()); \
13880 return NS_OK; \
13881 } \
13882 } \
13883 vp.setNull(); \
13884 return NS_OK; \
13885 } \
13886 NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \
13887 JS::Handle<JS::Value> v) { \
13888 EventListenerManager *elm = GetOrCreateListenerManager(); \
13889 if (!elm) { \
13890 return NS_ERROR_OUT_OF_MEMORY; \
13891 } \
13892 \
13893 nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler; \
13894 JS::Rooted<JSObject*> callable(cx); \
13895 if (v.isObject() && \
13896 JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
13897 handler = new OnBeforeUnloadEventHandlerNonNull(callable, GetIncumbentGlobal()); \
13898 } \
13899 elm->SetEventHandler(handler); \
13900 return NS_OK; \
13901 }
13902 #define WINDOW_ONLY_EVENT EVENT
13903 #define TOUCH_EVENT EVENT
13904 #include "mozilla/EventNameList.h"
13905 #undef TOUCH_EVENT
13906 #undef WINDOW_ONLY_EVENT
13907 #undef BEFOREUNLOAD_EVENT
13908 #undef ERROR_EVENT
13909 #undef EVENT
13911 #ifdef _WINDOWS_
13912 #error "Never include windows.h in this file!"
13913 #endif