dom/base/nsGlobalWindow.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:bb58c377186e
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/. */
6
7 #include "nsGlobalWindow.h"
8
9 #include <algorithm>
10
11 #include "mozilla/MemoryReporting.h"
12
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"
39
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"
52
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"
68
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"
149
150 #include "xpcprivate.h"
151
152 #ifdef NS_PRINTING
153 #include "nsIPrintSettings.h"
154 #include "nsIPrintSettingsService.h"
155 #include "nsIWebBrowserPrint.h"
156 #endif
157
158 #include "nsWindowRoot.h"
159 #include "nsNetCID.h"
160 #include "nsIArray.h"
161
162 // XXX An unfortunate dependency exists here (two XUL files).
163 #include "nsIDOMXULDocument.h"
164 #include "nsIDOMXULCommandDispatcher.h"
165
166 #include "nsBindingManager.h"
167 #include "nsXBLService.h"
168
169 // used for popup blocking, needs to be converted to something
170 // belonging to the back-end like nsIContentPolicy
171 #include "nsIPopupWindowManager.h"
172
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"
189
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"
195
196 #include "mozilla/dom/StructuredCloneTags.h"
197
198 #ifdef MOZ_GAMEPAD
199 #include "mozilla/dom/GamepadService.h"
200 #endif
201
202 #include "nsRefreshDriver.h"
203
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
224
225 #ifdef MOZ_WEBSPEECH
226 #include "mozilla/dom/SpeechSynthesis.h"
227 #endif
228
229 #ifdef MOZ_JSDEBUGGER
230 #include "jsdIDebuggerService.h"
231 #endif
232
233 #ifdef MOZ_B2G
234 #include "nsPISocketTransportService.h"
235 #endif
236
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"
243
244 #ifdef ANDROID
245 #include <android/log.h>
246 #endif
247
248 #ifdef PR_LOGGING
249 static PRLogModuleInfo* gDOMLeakPRLog;
250 #endif
251
252 #ifdef XP_WIN
253 #include <process.h>
254 #define getpid _getpid
255 #else
256 #include <unistd.h> // for getpid()
257 #endif
258
259 static const char kStorageEnabled[] = "dom.storage.enabled";
260
261 using namespace mozilla;
262 using namespace mozilla::dom;
263 using namespace mozilla::dom::ipc;
264 using mozilla::TimeStamp;
265 using mozilla::TimeDuration;
266
267 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
268 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
269 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
270
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)
284
285 #ifdef DEBUG_jst
286 int32_t gTimeoutCnt = 0;
287 #endif
288
289 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
290 #define DEBUG_PAGE_CACHE
291 #endif
292
293 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
294
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 }
306
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
310
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
315
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
329
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
347
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
362
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
376
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
387
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
401
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
412
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
424
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
435
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
447
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
467
468 // CIDs
469 static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
470
471 static const char sPopStatePrefStr[] = "browser.history.allowPopState";
472
473 #define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload")
474 #define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload")
475
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 }
500
501 private:
502 nsGlobalWindow* mWindow;
503 };
504
505 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
506
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;
520
521 ++gTimeoutCnt;
522 }
523 #endif
524
525 MOZ_COUNT_CTOR(nsTimeout);
526 }
527
528 nsTimeout::~nsTimeout()
529 {
530 #ifdef DEBUG_jst
531 {
532 extern int gTimeoutCnt;
533
534 --gTimeoutCnt;
535 }
536 #endif
537
538 if (mTimer) {
539 mTimer->Cancel();
540 mTimer = nullptr;
541 }
542
543 MOZ_COUNT_DTOR(nsTimeout);
544 }
545
546 NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout)
547
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)
556
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 }
564
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 {}
580
581 nsPIDOMWindow::~nsPIDOMWindow() {}
582
583 // DialogValueHolder CC goop.
584 NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue)
585
586 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
587 NS_INTERFACE_MAP_ENTRY(nsISupports)
588 NS_INTERFACE_MAP_END
589
590 NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder)
591 NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder)
592
593 //*****************************************************************************
594 // nsOuterWindowProxy: Outer Window Proxy
595 //*****************************************************************************
596
597 class nsOuterWindowProxy : public js::Wrapper
598 {
599 public:
600 nsOuterWindowProxy() : js::Wrapper(0) { }
601
602 virtual bool finalizeInBackground(JS::Value priv) {
603 return false;
604 }
605
606 virtual const char *className(JSContext *cx,
607 JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
608 virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
609
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;
635
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;
640
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;
660
661 static nsOuterWindowProxy singleton;
662
663 protected:
664 nsGlobalWindow* GetWindow(JSObject *proxy)
665 {
666 return nsGlobalWindow::FromSupports(
667 static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
668 }
669
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);
676
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);
682
683 bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
684 JS::AutoIdVector &props);
685 };
686
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 ));
700
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 }
711
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 }
721
722 const char *
723 nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy)
724 {
725 MOZ_ASSERT(js::IsProxy(proxy));
726
727 return "Window";
728 }
729
730 void
731 nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy)
732 {
733 nsGlobalWindow* global = GetWindow(proxy);
734 if (global) {
735 global->ClearWrapper();
736
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 }
744
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 }
758
759 if (desc.object()) {
760 return true;
761 }
762
763 return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc);
764 }
765
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
781
782 return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
783 }
784
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 }
799
800 return js::Wrapper::defineProperty(cx, proxy, id, desc);
801 }
802
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 }
812
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 }
819
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 }
830
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 }
837
838 return js::Wrapper::delete_(cx, proxy, id, bp);
839 }
840
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 }
849
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 }
856
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 }
865
866 return js::Wrapper::has(cx, proxy, id, bp);
867 }
868
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 }
877
878 return js::Wrapper::hasOwn(cx, proxy, id, bp);
879 }
880
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 }
892
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
901
902 return js::Wrapper::get(cx, proxy, receiver, id, vp);
903 }
904
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 }
920
921 return js::Wrapper::set(cx, proxy, receiver, id, strict, vp);
922 }
923
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 }
932
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 }
941
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 }
954
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 }
966
967 vp.setObject(*obj);
968 return JS_WrapValue(cx, vp);
969 }
970
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 }
980
981 nsGlobalWindow* win = GetWindow(proxy);
982 bool unused;
983 return win->IndexedGetter(index, unused);
984 }
985
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 }
998
999 return true;
1000 }
1001
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 }
1008
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 }
1015
1016 nsOuterWindowProxy
1017 nsOuterWindowProxy::singleton;
1018
1019 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
1020 {
1021 public:
1022 nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
1023
1024 virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
1025
1026 static nsChromeOuterWindowProxy singleton;
1027 };
1028
1029 const char *
1030 nsChromeOuterWindowProxy::className(JSContext *cx,
1031 JS::Handle<JSObject*> proxy)
1032 {
1033 MOZ_ASSERT(js::IsProxy(proxy));
1034
1035 return "ChromeWindow";
1036 }
1037
1038 nsChromeOuterWindowProxy
1039 nsChromeOuterWindowProxy::singleton;
1040
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);
1052
1053 NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class");
1054 return obj;
1055 }
1056
1057 //*****************************************************************************
1058 //*** nsGlobalWindow: Object Management
1059 //*****************************************************************************
1060
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();
1117
1118 // Initialize the PRCList (this).
1119 PR_INIT_CLIST(this);
1120
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);
1125
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);
1135
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();
1146
1147 mObserver = nullptr;
1148 SetIsDOMBinding();
1149 }
1150
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.
1154
1155 gRefCnt++;
1156
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 }
1168
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 }
1180
1181 mSerial = ++gSerialCounter;
1182
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
1193
1194 #ifdef PR_LOGGING
1195 if (gDOMLeakPRLog)
1196 PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
1197 ("DOMWINDOW %p created outer=%p", this, aOuterWindow));
1198 #endif
1199
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 }
1208
1209 /* static */
1210 void
1211 nsGlobalWindow::Init()
1212 {
1213 CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector);
1214 NS_ASSERTION(gEntropyCollector,
1215 "gEntropyCollector should have been initialized!");
1216
1217 #ifdef PR_LOGGING
1218 gDOMLeakPRLog = PR_NewLogModule("DOMLeak");
1219 NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");
1220 #endif
1221
1222 sWindowsById = new WindowByIdTable();
1223 }
1224
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 }
1233
1234 nsGlobalWindow::~nsGlobalWindow()
1235 {
1236 mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
1237 mEventTargetObjects.Clear();
1238
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 }
1246
1247 --gRefCnt;
1248
1249 #ifdef DEBUG
1250 if (!PR_GetEnv("MOZ_QUIET")) {
1251 nsAutoCString url;
1252 if (mLastOpenedURI) {
1253 mLastOpenedURI->GetSpec(url);
1254
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 }
1261
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
1272
1273 #ifdef PR_LOGGING
1274 if (gDOMLeakPRLog)
1275 PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG,
1276 ("DOMWINDOW %p destroyed", this));
1277 #endif
1278
1279 if (IsOuterWindow()) {
1280 JSObject *proxy = GetWrapperPreserveColor();
1281 if (proxy) {
1282 js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr));
1283 }
1284
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.
1289
1290 nsGlobalWindow *w;
1291 while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) {
1292 PR_REMOVE_AND_INIT_LINK(w);
1293 }
1294
1295 DropOuterWindowDocs();
1296 } else {
1297 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
1298 mMutationBits ? 1 : 0);
1299
1300 if (mListenerManager) {
1301 mListenerManager->Disconnect();
1302 mListenerManager = nullptr;
1303 }
1304
1305 // An inner window is destroyed, pull it out of the outer window's
1306 // list if inner windows.
1307
1308 PR_REMOVE_LINK(this);
1309
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 }
1317
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 }
1328
1329 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
1330 if (ac)
1331 ac->RemoveWindowAsListener(this);
1332
1333 nsLayoutStatics::Release();
1334 }
1335
1336 void
1337 nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject)
1338 {
1339 mEventTargetObjects.PutEntry(aObject);
1340 }
1341
1342 void
1343 nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
1344 {
1345 mEventTargetObjects.RemoveEntry(aObject);
1346 }
1347
1348 // static
1349 void
1350 nsGlobalWindow::ShutDown()
1351 {
1352 if (gDumpFile && gDumpFile != stdout) {
1353 fclose(gDumpFile);
1354 }
1355 gDumpFile = nullptr;
1356
1357 NS_IF_RELEASE(gEntropyCollector);
1358
1359 delete sWindowsById;
1360 sWindowsById = nullptr;
1361 }
1362
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 }
1372
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 }
1385
1386 void
1387 nsGlobalWindow::DropOuterWindowDocs()
1388 {
1389 MOZ_ASSERT(IsOuterWindow());
1390 MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed());
1391 mDoc = nullptr;
1392 mSuspendedDoc = nullptr;
1393 }
1394
1395 void
1396 nsGlobalWindow::CleanUp()
1397 {
1398 // Guarantee idempotence.
1399 if (mCleanedUp)
1400 return;
1401 mCleanedUp = true;
1402
1403 mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
1404 mEventTargetObjects.Clear();
1405
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 }
1412
1413 #ifdef MOZ_B2G
1414 DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
1415 DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
1416 #endif // MOZ_B2G
1417
1418 if (mIdleService) {
1419 mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
1420 }
1421
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 }
1427
1428 if (mNavigator) {
1429 mNavigator->Invalidate();
1430 mNavigator = nullptr;
1431 }
1432
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;
1446
1447 mConsole = nullptr;
1448
1449 mExternal = nullptr;
1450
1451 mPerformance = nullptr;
1452
1453 #ifdef MOZ_WEBSPEECH
1454 mSpeechSynthesis = nullptr;
1455 #endif
1456
1457 ClearControllers();
1458
1459 mOpener = nullptr; // Forces Release
1460 if (mContext) {
1461 mContext = nullptr; // Forces Release
1462 }
1463 mChromeEventHandler = nullptr; // Forces Release
1464 mParentTarget = nullptr;
1465
1466 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
1467
1468 if (inner) {
1469 inner->CleanUp();
1470 }
1471
1472 DisableGamepadUpdates();
1473 mHasGamepad = false;
1474
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 }
1483
1484 mArguments = nullptr;
1485 mDialogArguments = nullptr;
1486
1487 CleanupCachedXBLHandlers(this);
1488
1489 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1490 mAudioContexts[i]->Shutdown();
1491 }
1492 mAudioContexts.Clear();
1493
1494 if (mIdleTimer) {
1495 mIdleTimer->Cancel();
1496 mIdleTimer = nullptr;
1497 }
1498
1499 DisableTimeChangeNotifications();
1500 }
1501
1502 void
1503 nsGlobalWindow::ClearControllers()
1504 {
1505 if (mControllers) {
1506 uint32_t count;
1507 mControllers->GetControllerCount(&count);
1508
1509 while (count--) {
1510 nsCOMPtr<nsIController> controller;
1511 mControllers->GetControllerAt(count, getter_AddRefs(controller));
1512
1513 nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller);
1514 if (context)
1515 context->SetCommandContext(nullptr);
1516 }
1517
1518 mControllers = nullptr;
1519 }
1520 }
1521
1522 void
1523 nsGlobalWindow::FreeInnerObjects()
1524 {
1525 NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
1526
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);
1531
1532 mInnerObjectsFreed = true;
1533
1534 // Kill all of the workers for this window.
1535 mozilla::dom::workers::CancelWorkersForWindow(this);
1536
1537 // Close all offline storages for this window.
1538 quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
1539 if (quotaManager) {
1540 quotaManager->AbortCloseStoragesForWindow(this);
1541 }
1542
1543 ClearAllTimeouts();
1544
1545 if (mIdleTimer) {
1546 mIdleTimer->Cancel();
1547 mIdleTimer = nullptr;
1548 }
1549
1550 mIdleObservers.Clear();
1551
1552 mChromeEventHandler = nullptr;
1553
1554 if (mListenerManager) {
1555 mListenerManager->Disconnect();
1556 mListenerManager = nullptr;
1557 }
1558
1559 mLocation = nullptr;
1560 mHistory = nullptr;
1561
1562 if (mNavigator) {
1563 mNavigator->OnNavigation();
1564 mNavigator->Invalidate();
1565 mNavigator = nullptr;
1566 }
1567
1568 if (mScreen) {
1569 mScreen = nullptr;
1570 }
1571
1572 if (mDoc) {
1573 // Remember the document's principal and URI.
1574 mDocumentPrincipal = mDoc->NodePrincipal();
1575 mDocumentURI = mDoc->GetDocumentURI();
1576 mDocBaseURI = mDoc->GetDocBaseURI();
1577
1578 while (mDoc->EventHandlingSuppressed()) {
1579 mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false);
1580 }
1581
1582 // Note: we don't have to worry about eAnimationsOnly suppressions because
1583 // they won't leak.
1584 }
1585
1586 // Remove our reference to the document and the document principal.
1587 mFocusedNode = nullptr;
1588
1589 if (mApplicationCache) {
1590 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect();
1591 mApplicationCache = nullptr;
1592 }
1593
1594 mIndexedDB = nullptr;
1595
1596 NotifyWindowIDDestroyed("inner-window-destroyed");
1597
1598 CleanupCachedXBLHandlers(this);
1599
1600 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
1601 mAudioContexts[i]->Shutdown();
1602 }
1603 mAudioContexts.Clear();
1604
1605 #ifdef MOZ_GAMEPAD
1606 DisableGamepadUpdates();
1607 mHasGamepad = false;
1608 mGamepads.Clear();
1609 #endif
1610 }
1611
1612 //*****************************************************************************
1613 // nsGlobalWindow::nsISupports
1614 //*****************************************************************************
1615
1616 DOMCI_DATA(Window, nsGlobalWindow)
1617
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
1654
1655
1656 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
1657 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
1658
1659 static PLDHashOperator
1660 MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
1661 {
1662 JS::ExposeObjectToActiveJS(aData);
1663 return PL_DHASH_NEXT;
1664 }
1665
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
1678
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
1682
1683 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow)
1684 return tmp->IsBlackForCC(false);
1685 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1686
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 }
1695
1696 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow)
1697
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 }
1706
1707 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
1708
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)
1714
1715 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
1716
1717 #ifdef MOZ_WEBSPEECH
1718 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
1719 #endif
1720
1721 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
1722
1723 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1724
1725 for (nsTimeout* timeout = tmp->mTimeouts.getFirst();
1726 timeout;
1727 timeout = timeout->getNext()) {
1728 cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
1729 }
1730
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)
1739
1740 #ifdef MOZ_GAMEPAD
1741 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
1742 #endif
1743
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)
1749
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
1761
1762 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
1763 nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
1764
1765 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
1766
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)
1772
1773 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
1774
1775 #ifdef MOZ_WEBSPEECH
1776 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
1777 #endif
1778
1779 if (tmp->mOuterWindow) {
1780 static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp);
1781 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
1782 }
1783
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)
1799
1800 #ifdef MOZ_GAMEPAD
1801 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
1802 #endif
1803
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)
1809
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
1821
1822 #ifdef DEBUG
1823 void
1824 nsGlobalWindow::RiskyUnlink()
1825 {
1826 NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
1827 }
1828 #endif
1829
1830 struct TraceData
1831 {
1832 const TraceCallbacks& callbacks;
1833 void* closure;
1834 };
1835
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 }
1843
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
1851
1852 bool
1853 nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
1854 {
1855 if (!nsCCUncollectableMarker::sGeneration) {
1856 return false;
1857 }
1858
1859 return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
1860 IsBlack()) &&
1861 (!aTracingNeeded ||
1862 HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
1863 }
1864
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 }
1881
1882 //*****************************************************************************
1883 // nsGlobalWindow::nsIScriptGlobalObject
1884 //*****************************************************************************
1885
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 }
1894
1895 if (outer->GetWrapperPreserveColor()) {
1896 return NS_OK;
1897 }
1898
1899 NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(),
1900 "No cached wrapper, but we have an inner window?");
1901
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);
1906
1907 NS_ASSERTION(!outer->mContext, "Will overwrite mContext!");
1908
1909 // should probably assert the context is clean???
1910 context->WillInitializeContext();
1911
1912 nsresult rv = context->InitContext();
1913 NS_ENSURE_SUCCESS(rv, rv);
1914
1915 outer->mContext = context;
1916 return NS_OK;
1917 }
1918
1919 nsIScriptContext *
1920 nsGlobalWindow::GetScriptContext()
1921 {
1922 nsGlobalWindow* outer = GetOuterWindowInternal();
1923 return outer ? outer->mContext : nullptr;
1924 }
1925
1926 JSObject *
1927 nsGlobalWindow::GetGlobalJSObject()
1928 {
1929 return FastGetGlobalJSObject();
1930 }
1931
1932 void
1933 nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
1934 {
1935 TraceWrapper(aTrc, "active window global");
1936 }
1937
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();
1945
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 }
1955
1956 JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject());
1957 MOZ_ASSERT(winObj);
1958
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 }
1967
1968 return winObj;
1969 }
1970
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.
1980
1981 if (!mDoc || !aNewDocument) {
1982 return false;
1983 }
1984
1985 if (!mDoc->IsInitialDocument()) {
1986 return false;
1987 }
1988
1989 NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()),
1990 "How'd this happen?");
1991
1992 // Great, we're the original document, check for one of the other
1993 // conditions.
1994
1995 if (mDoc == aNewDocument) {
1996 return true;
1997 }
1998
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 }
2006
2007 return false;
2008 }
2009
2010 void
2011 nsGlobalWindow::SetInitialPrincipalToSubject()
2012 {
2013 FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ());
2014
2015 // First, grab the subject principal.
2016 nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::GetSubjectPrincipal();
2017 if (!newWindowPrincipal) {
2018 newWindowPrincipal = nsContentUtils::GetSystemPrincipal();
2019 }
2020
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 }
2027
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;
2036
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 }
2047
2048 GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal);
2049 mDoc->SetIsInitialDocument(true);
2050
2051 nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell();
2052
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 }
2060
2061 PopupControlState
2062 PushPopupControlState(PopupControlState aState, bool aForce)
2063 {
2064 MOZ_ASSERT(NS_IsMainThread());
2065
2066 PopupControlState oldState = gPopupControlState;
2067
2068 if (aState < gPopupControlState || aForce) {
2069 gPopupControlState = aState;
2070 }
2071
2072 return oldState;
2073 }
2074
2075 void
2076 PopPopupControlState(PopupControlState aState)
2077 {
2078 MOZ_ASSERT(NS_IsMainThread());
2079
2080 gPopupControlState = aState;
2081 }
2082
2083 PopupControlState
2084 nsGlobalWindow::PushPopupControlState(PopupControlState aState,
2085 bool aForce) const
2086 {
2087 return ::PushPopupControlState(aState, aForce);
2088 }
2089
2090 void
2091 nsGlobalWindow::PopPopupControlState(PopupControlState aState) const
2092 {
2093 ::PopPopupControlState(aState);
2094 }
2095
2096 PopupControlState
2097 nsGlobalWindow::GetPopupControlState() const
2098 {
2099 MOZ_ASSERT(NS_IsMainThread());
2100 return gPopupControlState;
2101 }
2102
2103 #define WINDOWSTATEHOLDER_IID \
2104 {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}}
2105
2106 class WindowStateHolder MOZ_FINAL : public nsISupports
2107 {
2108 public:
2109 NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
2110 NS_DECL_ISUPPORTS
2111
2112 WindowStateHolder(nsGlobalWindow *aWindow);
2113
2114 nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
2115
2116 void DidRestoreWindow()
2117 {
2118 mInnerWindow = nullptr;
2119 }
2120
2121 protected:
2122 ~WindowStateHolder();
2123
2124 nsRefPtr<nsGlobalWindow> mInnerWindow;
2125 };
2126
2127 NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
2128
2129 WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow)
2130 : mInnerWindow(aWindow)
2131 {
2132 NS_PRECONDITION(aWindow, "null window");
2133 NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
2134
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));
2138
2139 aWindow->SuspendTimeouts();
2140
2141 // When a global goes into the bfcache, we disable script.
2142 xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false);
2143 }
2144
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 }
2156
2157 NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder)
2158
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 }
2169
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);
2186
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");
2191
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 }
2202
2203 nsIXPConnect* xpc = nsContentUtils::XPConnect();
2204
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;
2210
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);
2216
2217 aGlobal.set(holder->GetJSObject());
2218
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);
2224
2225 return NS_OK;
2226 }
2227
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);
2236
2237 if (IsInnerWindow()) {
2238 if (!mOuterWindow) {
2239 return NS_ERROR_NOT_INITIALIZED;
2240 }
2241
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 }
2247
2248 return GetOuterWindowInternal()->SetNewDocument(aDocument, aState,
2249 aForceReuseInnerWindow);
2250 }
2251
2252 NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows");
2253
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.
2257
2258 Thaw();
2259 }
2260
2261 NS_ASSERTION(!GetCurrentInnerWindow() ||
2262 GetCurrentInnerWindow()->GetExtantDoc() == mDoc,
2263 "Uh, mDoc doesn't match the current inner window "
2264 "document!");
2265
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 }
2274
2275 nsCOMPtr<nsIDocument> oldDoc = mDoc;
2276
2277 nsIScriptContext *scx = GetContextInternal();
2278 NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED);
2279
2280 JSContext *cx = scx->GetNativeContext();
2281
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
2289
2290 if (!mDoc) {
2291 // First document load.
2292
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();
2297
2298 if (privateRoot == static_cast<nsIDOMWindow*>(this)) {
2299 nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler);
2300 }
2301 }
2302
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.) */
2306
2307 nsContentUtils::AddScriptRunner(
2308 NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus));
2309
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();
2314
2315 nsresult rv = NS_OK;
2316
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 }
2324
2325 // Take this opportunity to clear mSuspendedDoc. Our old inner window is now
2326 // responsible for unsuspending it.
2327 mSuspendedDoc = nullptr;
2328
2329 #ifdef DEBUG
2330 mLastOpenedURI = aDocument->GetDocumentURI();
2331 #endif
2332
2333 mContext->WillInitializeContext();
2334
2335 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
2336
2337 if (currentInner && currentInner->mNavigator) {
2338 currentInner->mNavigator->OnNavigation();
2339 }
2340
2341 nsRefPtr<nsGlobalWindow> newInnerWindow;
2342 bool createdInnerWindow = false;
2343
2344 bool thisChrome = IsChromeWindow();
2345
2346 nsCxPusher cxPusher;
2347 cxPusher.Push(cx);
2348
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);
2352
2353 nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
2354 NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
2355
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();
2363
2364 if (aDocument != oldDoc) {
2365 JS::ExposeObjectToActiveJS(newInnerGlobal);
2366 }
2367
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 }
2376
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 }
2404
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].
2414
2415 mInnerWindow = nullptr;
2416
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");
2428
2429 mCreatingInnerWindow = false;
2430 createdInnerWindow = true;
2431 Thaw();
2432
2433 NS_ENSURE_SUCCESS(rv, rv);
2434 }
2435
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 }
2446
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 }
2459
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 }
2466
2467 mInnerWindow = newInnerWindow;
2468
2469 if (!GetWrapperPreserveColor()) {
2470 JS::Rooted<JSObject*> outer(cx,
2471 NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
2472 NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
2473
2474 js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
2475
2476 // Inform the nsJSContext, which is the canonical holder of the outer.
2477 mContext->SetWindowProxy(outer);
2478 mContext->DidInitializeContext();
2479
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 }
2489
2490 JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
2491
2492 js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
2493
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 }
2499
2500 js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
2501
2502 SetWrapper(outerObject);
2503
2504 {
2505 JSAutoCompartment ac(cx, outerObject);
2506
2507 JS_SetParent(cx, outerObject, newInnerGlobal);
2508
2509 // Inform the nsJSContext, which is the canonical holder of the outer.
2510 mContext->SetWindowProxy(outerObject);
2511
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 }
2520
2521 // Enter the new global's compartment.
2522 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
2523
2524 // Set scriptability based on the state of the docshell.
2525 bool allow = GetDocShell()->GetCanExecuteScripts();
2526 xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
2527
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 }
2540
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 }
2551
2552 JSAutoCompartment ac(cx, GetWrapperPreserveColor());
2553
2554 if (!aState && !reUseInnerWindow) {
2555 // Loading a new page and creating a new inner window, *not*
2556 // restoring from session history.
2557
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
2569
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 }
2578
2579 // Add an extra ref in case we release mContext during GC.
2580 nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
2581
2582 aDocument->SetScriptGlobalObject(newInnerWindow);
2583
2584 if (!aState) {
2585 if (reUseInnerWindow) {
2586 if (newInnerWindow->mDoc != aDocument) {
2587 newInnerWindow->mDoc = aDocument;
2588
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.
2595
2596 JS::Rooted<JSObject*> obj(cx,
2597 currentInner->GetWrapperPreserveColor());
2598 ::JS_DeleteProperty(cx, obj, "document");
2599 }
2600 }
2601 } else {
2602 newInnerWindow->InnerSetNewDocument(cx, aDocument);
2603
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 }
2609
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 }
2617
2618 if (mArguments) {
2619 newInnerWindow->DefineArgumentsProperty(mArguments);
2620 mArguments = nullptr;
2621 }
2622
2623 // Give the new inner window our chrome event handler (since it
2624 // doesn't have one).
2625 newInnerWindow->mChromeEventHandler = mChromeEventHandler;
2626 }
2627
2628 mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
2629 mContext->DidInitializeContext();
2630
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 }
2637
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 }
2651
2652 PreloadLocalStorage();
2653
2654 return NS_OK;
2655 }
2656
2657 void
2658 nsGlobalWindow::PreloadLocalStorage()
2659 {
2660 if (!Preferences::GetBool(kStorageEnabled)) {
2661 return;
2662 }
2663
2664 if (IsChromeWindow()) {
2665 return;
2666 }
2667
2668 nsIPrincipal* principal = GetPrincipal();
2669 if (!principal) {
2670 return;
2671 }
2672
2673 nsresult rv;
2674 nsCOMPtr<nsIURI> firstPartyIsolationURI;
2675 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
2676 if (NS_FAILED(rv)) {
2677 return;
2678 }
2679
2680 nsCOMPtr<nsIDOMStorageManager> storageManager =
2681 do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
2682 if (NS_FAILED(rv)) {
2683 return;
2684 }
2685
2686 storageManager->PrecacheStorageForFirstParty(firstPartyIsolationURI, principal);
2687 }
2688
2689 void
2690 nsGlobalWindow::DispatchDOMWindowCreated()
2691 {
2692 if (!mDoc) {
2693 return;
2694 }
2695
2696 // Fire DOMWindowCreated at chrome event listeners
2697 nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"),
2698 true /* bubbles */,
2699 false /* not cancellable */);
2700
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 }
2715
2716 void
2717 nsGlobalWindow::ClearStatus()
2718 {
2719 SetStatus(EmptyString());
2720 }
2721
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);
2727
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
2737
2738 mDoc = aDocument;
2739 if (IsDOMBinding()) {
2740 WindowBinding::ClearCachedDocumentValue(aCx, this);
2741 }
2742 mFocusedNode = nullptr;
2743 mLocalStorage = nullptr;
2744 mSessionStorage = nullptr;
2745
2746 #ifdef DEBUG
2747 mLastOpenedURI = aDocument->GetDocumentURI();
2748 #endif
2749
2750 Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
2751 mMutationBits ? 1 : 0);
2752
2753 // Clear our mutation bitfield.
2754 mMutationBits = 0;
2755 }
2756
2757 void
2758 nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
2759 {
2760 NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!");
2761 MOZ_ASSERT(aDocShell);
2762
2763 if (aDocShell == mDocShell) {
2764 return;
2765 }
2766
2767 mDocShell = aDocShell; // Weak Reference
2768
2769 NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
2770
2771 if (mFrames) {
2772 mFrames->SetDocShell(aDocShell);
2773 }
2774
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 }
2797
2798 bool docShellActive;
2799 mDocShell->GetIsActive(&docShellActive);
2800 mIsBackground = !docShellActive;
2801 }
2802
2803 void
2804 nsGlobalWindow::DetachFromDocShell()
2805 {
2806 NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");
2807
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).
2812
2813 NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!");
2814
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 }
2825
2826 // Make sure that this is called before we null out the document.
2827 NotifyDOMWindowDestroyed(this);
2828
2829 NotifyWindowIDDestroyed("outer-window-destroyed");
2830
2831 nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
2832
2833 if (currentInner) {
2834 NS_ASSERTION(mDoc, "Must have doc!");
2835
2836 // Remember the document's principal and URI.
2837 mDocumentPrincipal = mDoc->NodePrincipal();
2838 mDocumentURI = mDoc->GetDocumentURI();
2839 mDocBaseURI = mDoc->GetDocBaseURI();
2840
2841 // Release our document reference
2842 DropOuterWindowDocs();
2843 mFocusedNode = nullptr;
2844 }
2845
2846 ClearControllers();
2847
2848 mChromeEventHandler = nullptr; // force release now
2849
2850 if (mContext) {
2851 mContext->GC(JS::gcreason::SET_DOC_SHELL);
2852 mContext = nullptr;
2853 }
2854
2855 mDocShell = nullptr; // Weak Reference
2856
2857 NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
2858
2859 if (mFrames) {
2860 mFrames->SetDocShell(nullptr);
2861 }
2862
2863 MaybeForgiveSpamCount();
2864 CleanUp();
2865 }
2866
2867 void
2868 nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener,
2869 bool aOriginalOpener)
2870 {
2871 FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener));
2872
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");
2878
2879 mOpener = do_GetWeakReference(aOpener);
2880 NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
2881
2882 if (aOriginalOpener) {
2883 mHadOriginalOpener = true;
2884 }
2885
2886 #ifdef DEBUG
2887 mSetOpenerWindowCalled = true;
2888 #endif
2889 }
2890
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 }
2899
2900 nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
2901 if (!frameLoader) {
2902 return nullptr;
2903 }
2904
2905 nsCOMPtr<EventTarget> target = frameLoader->GetTabChildGlobalAsEventTarget();
2906 return target.forget();
2907 }
2908
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.
2916
2917 nsCOMPtr<Element> frameElement = GetFrameElementInternal();
2918 nsCOMPtr<EventTarget> eventTarget =
2919 TryGetTabChildGlobalAsEventTarget(frameElement);
2920
2921 if (!eventTarget) {
2922 nsGlobalWindow* topWin = GetScriptableTop();
2923 if (topWin) {
2924 frameElement = topWin->GetFrameElementInternal();
2925 eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement);
2926 }
2927 }
2928
2929 if (!eventTarget) {
2930 eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler);
2931 }
2932
2933 if (!eventTarget) {
2934 eventTarget = mChromeEventHandler;
2935 }
2936
2937 mParentTarget = eventTarget;
2938 }
2939
2940 EventTarget*
2941 nsGlobalWindow::GetTargetForDOMEvent()
2942 {
2943 return GetOuterWindowInternal();
2944 }
2945
2946 EventTarget*
2947 nsGlobalWindow::GetTargetForEventTargetChain()
2948 {
2949 return IsInnerWindow() ? this : GetCurrentInnerWindowInternal();
2950 }
2951
2952 nsresult
2953 nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor)
2954 {
2955 return NS_OK;
2956 }
2957
2958 JSContext*
2959 nsGlobalWindow::GetJSContextForEventHandlers()
2960 {
2961 return nullptr;
2962 }
2963
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;
2970
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];
2981
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 }
3006
3007 aVisitor.mParentTarget = GetParentTarget();
3008
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 }
3016
3017 return NS_OK;
3018 }
3019
3020 bool
3021 nsGlobalWindow::ShouldPromptToBlockDialogs()
3022 {
3023 MOZ_ASSERT(IsOuterWindow());
3024
3025 nsGlobalWindow *topWindow = GetScriptableTop();
3026 if (!topWindow) {
3027 NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?");
3028 return true;
3029 }
3030
3031 topWindow = topWindow->GetCurrentInnerWindowInternal();
3032 if (!topWindow) {
3033 return true;
3034 }
3035
3036 return topWindow->DialogsAreBeingAbused();
3037 }
3038
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 }
3047
3048 // TODO: Warn if no top window?
3049 topWindow = topWindow->GetCurrentInnerWindowInternal();
3050 if (!topWindow) {
3051 return false;
3052 }
3053
3054 // Dialogs are blocked if the content viewer is hidden
3055 if (mDocShell) {
3056 nsCOMPtr<nsIContentViewer> cv;
3057 mDocShell->GetContentViewer(getter_AddRefs(cv));
3058
3059 bool isHidden;
3060 cv->GetIsHidden(&isHidden);
3061 if (isHidden) {
3062 return false;
3063 }
3064 }
3065
3066 return topWindow->mAreDialogsEnabled;
3067 }
3068
3069 bool
3070 nsGlobalWindow::DialogsAreBeingAbused()
3071 {
3072 MOZ_ASSERT(IsInnerWindow());
3073 NS_ASSERTION(GetScriptableTop() &&
3074 GetScriptableTop()->GetCurrentInnerWindowInternal() == this,
3075 "DialogsAreBeingAbused called with invalid window");
3076
3077 if (mLastDialogQuitTime.IsNull() ||
3078 nsContentUtils::IsCallerChrome()) {
3079 return false;
3080 }
3081
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++;
3087
3088 return GetPopupControlState() > openAllowed ||
3089 mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
3090 }
3091
3092 // Reset the abuse counter
3093 mDialogAbuseCount = 0;
3094
3095 return false;
3096 }
3097
3098 bool
3099 nsGlobalWindow::ConfirmDialogIfNeeded()
3100 {
3101 MOZ_ASSERT(IsOuterWindow());
3102
3103 NS_ENSURE_TRUE(mDocShell, false);
3104 nsCOMPtr<nsIPromptService> promptSvc =
3105 do_GetService("@mozilla.org/embedcomp/prompt-service;1");
3106
3107 if (!promptSvc) {
3108 return true;
3109 }
3110
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);
3115
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 }
3127
3128 return true;
3129 }
3130
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 }
3139
3140 topWindow = topWindow->GetCurrentInnerWindowInternal();
3141 // TODO: Warn if no top window?
3142 if (topWindow) {
3143 topWindow->mAreDialogsEnabled = false;
3144 }
3145 }
3146
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 }
3155
3156 // TODO: Warn if no top window?
3157 topWindow = topWindow->GetCurrentInnerWindowInternal();
3158 if (topWindow) {
3159 topWindow->mAreDialogsEnabled = true;
3160 }
3161 }
3162
3163 nsresult
3164 nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor)
3165 {
3166 NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?");
3167
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 }
3177
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());
3183
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;
3199
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.
3206
3207 nsEventStatus status = nsEventStatus_eIgnore;
3208 WidgetEvent event(aVisitor.mEvent->mFlags.mIsTrusted, NS_LOAD);
3209 event.mFlags.mBubbles = false;
3210
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 }
3219
3220 return NS_OK;
3221 }
3222
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 }
3233
3234 void
3235 nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
3236 {
3237 MOZ_ASSERT(IsOuterWindow());
3238 if (aObject == GetWrapperPreserveColor()) {
3239 PoisonWrapper();
3240 }
3241 }
3242
3243 nsresult
3244 nsGlobalWindow::SetArguments(nsIArray *aArguments)
3245 {
3246 MOZ_ASSERT(IsOuterWindow());
3247 nsresult rv;
3248
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 }
3274
3275 return NS_OK;
3276 }
3277
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;
3285
3286 JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
3287 return ctx->SetProperty(obj, "arguments", aArguments);
3288 }
3289
3290 //*****************************************************************************
3291 // nsGlobalWindow::nsIScriptObjectPrincipal
3292 //*****************************************************************************
3293
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 }
3301
3302 if (mDocumentPrincipal) {
3303 return mDocumentPrincipal;
3304 }
3305
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.
3311
3312 nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
3313 do_QueryInterface(GetParentInternal());
3314
3315 if (objPrincipal) {
3316 return objPrincipal->GetPrincipal();
3317 }
3318
3319 return nullptr;
3320 }
3321
3322 //*****************************************************************************
3323 // nsGlobalWindow::nsIDOMWindow
3324 //*****************************************************************************
3325
3326 nsIURI*
3327 nsPIDOMWindow::GetDocumentURI() const
3328 {
3329 return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get();
3330 }
3331
3332 nsIURI*
3333 nsPIDOMWindow::GetDocBaseURI() const
3334 {
3335 return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get();
3336 }
3337
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 }
3349
3350 Element*
3351 nsPIDOMWindow::GetFrameElementInternal() const
3352 {
3353 if (mOuterWindow) {
3354 return mOuterWindow->GetFrameElementInternal();
3355 }
3356
3357 NS_ASSERTION(!IsInnerWindow(),
3358 "GetFrameElementInternal() called on orphan inner window");
3359
3360 return mFrameElement;
3361 }
3362
3363 void
3364 nsPIDOMWindow::SetFrameElementInternal(Element* aFrameElement)
3365 {
3366 if (IsOuterWindow()) {
3367 mFrameElement = aFrameElement;
3368
3369 return;
3370 }
3371
3372 if (!mOuterWindow) {
3373 NS_ERROR("frameElement set on inner window with no outer!");
3374
3375 return;
3376 }
3377
3378 mOuterWindow->SetFrameElementInternal(aFrameElement);
3379 }
3380
3381 void
3382 nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
3383 {
3384 mAudioContexts.AppendElement(aAudioContext);
3385
3386 nsIDocShell* docShell = GetDocShell();
3387 if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) {
3388 aAudioContext->Mute();
3389 }
3390 }
3391
3392 void
3393 nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext)
3394 {
3395 mAudioContexts.RemoveElement(aAudioContext);
3396 }
3397
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 }
3407
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 }
3417
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 }
3425
3426 nsIDOMWindow*
3427 nsGlobalWindow::GetWindow(ErrorResult& aError)
3428 {
3429 FORWARD_TO_OUTER_OR_THROW(GetWindow, (aError), aError, nullptr);
3430
3431 return this;
3432 }
3433
3434 NS_IMETHODIMP
3435 nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow)
3436 {
3437 ErrorResult rv;
3438 nsCOMPtr<nsIDOMWindow> window = GetWindow(rv);
3439 window.forget(aWindow);
3440
3441 return rv.ErrorCode();
3442 }
3443
3444 nsIDOMWindow*
3445 nsGlobalWindow::GetSelf(ErrorResult& aError)
3446 {
3447 FORWARD_TO_OUTER_OR_THROW(GetSelf, (aError), aError, nullptr);
3448
3449 return this;
3450 }
3451
3452 NS_IMETHODIMP
3453 nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow)
3454 {
3455 ErrorResult rv;
3456 nsCOMPtr<nsIDOMWindow> window = GetSelf(rv);
3457 window.forget(aWindow);
3458
3459 return rv.ErrorCode();
3460 }
3461
3462 Navigator*
3463 nsGlobalWindow::GetNavigator(ErrorResult& aError)
3464 {
3465 FORWARD_TO_INNER_OR_THROW(GetNavigator, (aError), aError, nullptr);
3466
3467 if (!mNavigator) {
3468 mNavigator = new Navigator(this);
3469 }
3470
3471 return mNavigator;
3472 }
3473
3474 NS_IMETHODIMP
3475 nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator)
3476 {
3477 ErrorResult rv;
3478 nsCOMPtr<nsIDOMNavigator> navigator = GetNavigator(rv);
3479 navigator.forget(aNavigator);
3480
3481 return rv.ErrorCode();
3482 }
3483
3484 nsScreen*
3485 nsGlobalWindow::GetScreen(ErrorResult& aError)
3486 {
3487 FORWARD_TO_INNER_OR_THROW(GetScreen, (aError), aError, nullptr);
3488
3489 if (!mScreen) {
3490 mScreen = nsScreen::Create(this);
3491 if (!mScreen) {
3492 aError.Throw(NS_ERROR_UNEXPECTED);
3493 return nullptr;
3494 }
3495 }
3496
3497 return mScreen;
3498 }
3499
3500 NS_IMETHODIMP
3501 nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen)
3502 {
3503 ErrorResult rv;
3504 nsRefPtr<nsScreen> screen = GetScreen(rv);
3505 screen.forget(aScreen);
3506
3507 return rv.ErrorCode();
3508 }
3509
3510 nsHistory*
3511 nsGlobalWindow::GetHistory(ErrorResult& aError)
3512 {
3513 FORWARD_TO_INNER_OR_THROW(GetHistory, (aError), aError, nullptr);
3514
3515 if (!mHistory) {
3516 mHistory = new nsHistory(this);
3517 }
3518
3519 return mHistory;
3520 }
3521
3522 NS_IMETHODIMP
3523 nsGlobalWindow::GetHistory(nsISupports** aHistory)
3524 {
3525 ErrorResult rv;
3526 nsCOMPtr<nsISupports> history = GetHistory(rv);
3527 history.forget(aHistory);
3528
3529 return rv.ErrorCode();
3530 }
3531
3532 nsPerformance*
3533 nsGlobalWindow::GetPerformance(ErrorResult& aError)
3534 {
3535 FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr);
3536
3537 nsPerformance* p = nsPIDOMWindow::GetPerformance();
3538 if (!p) {
3539 aError.Throw(NS_ERROR_FAILURE);
3540 }
3541 return p;
3542 }
3543
3544 NS_IMETHODIMP
3545 nsGlobalWindow::GetPerformance(nsISupports** aPerformance)
3546 {
3547 ErrorResult rv;
3548 nsCOMPtr<nsISupports> performance = GetPerformance(rv);
3549 performance.forget(aPerformance);
3550
3551 return rv.ErrorCode();
3552 }
3553
3554 nsPerformance*
3555 nsPIDOMWindow::GetPerformance()
3556 {
3557 MOZ_ASSERT(IsInnerWindow());
3558 CreatePerformanceObjectIfNeeded();
3559 return mPerformance;
3560 }
3561
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 }
3595
3596 bool
3597 nsPIDOMWindow::GetAudioMuted() const
3598 {
3599 if (!IsInnerWindow()) {
3600 return mInnerWindow->GetAudioMuted();
3601 }
3602
3603 return mAudioMuted;
3604 }
3605
3606 void
3607 nsPIDOMWindow::SetAudioMuted(bool aMuted)
3608 {
3609 if (!IsInnerWindow()) {
3610 mInnerWindow->SetAudioMuted(aMuted);
3611 return;
3612 }
3613
3614 if (mAudioMuted == aMuted) {
3615 return;
3616 }
3617
3618 mAudioMuted = aMuted;
3619 RefreshMediaElements();
3620 }
3621
3622 float
3623 nsPIDOMWindow::GetAudioVolume() const
3624 {
3625 if (!IsInnerWindow()) {
3626 return mInnerWindow->GetAudioVolume();
3627 }
3628
3629 return mAudioVolume;
3630 }
3631
3632 nsresult
3633 nsPIDOMWindow::SetAudioVolume(float aVolume)
3634 {
3635 if (!IsInnerWindow()) {
3636 return mInnerWindow->SetAudioVolume(aVolume);
3637 }
3638
3639 if (aVolume < 0.0) {
3640 return NS_ERROR_DOM_INDEX_SIZE_ERR;
3641 }
3642
3643 if (mAudioVolume == aVolume) {
3644 return NS_OK;
3645 }
3646
3647 mAudioVolume = aVolume;
3648 RefreshMediaElements();
3649 return NS_OK;
3650 }
3651
3652 float
3653 nsPIDOMWindow::GetAudioGlobalVolume()
3654 {
3655 float globalVolume = 1.0;
3656 nsCOMPtr<nsPIDOMWindow> window = this;
3657
3658 do {
3659 if (window->GetAudioMuted()) {
3660 return 0;
3661 }
3662
3663 globalVolume *= window->GetAudioVolume();
3664
3665 nsCOMPtr<nsIDOMWindow> win;
3666 window->GetParent(getter_AddRefs(win));
3667 if (window == win) {
3668 break;
3669 }
3670
3671 window = do_QueryInterface(win);
3672
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);
3676
3677 return globalVolume;
3678 }
3679
3680 void
3681 nsPIDOMWindow::RefreshMediaElements()
3682 {
3683 nsRefPtr<AudioChannelService> service =
3684 AudioChannelService::GetAudioChannelService();
3685 if (service) {
3686 service->RefreshAgentsVolume(this);
3687 }
3688 }
3689
3690 // nsISpeechSynthesisGetter
3691
3692 #ifdef MOZ_WEBSPEECH
3693 SpeechSynthesis*
3694 nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
3695 {
3696 FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr);
3697
3698 if (!mSpeechSynthesis) {
3699 mSpeechSynthesis = new SpeechSynthesis(this);
3700 }
3701
3702 return mSpeechSynthesis;
3703 }
3704
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);
3714
3715 return rv.ErrorCode();
3716 }
3717 #endif
3718
3719 already_AddRefed<nsIDOMWindow>
3720 nsGlobalWindow::GetParent(ErrorResult& aError)
3721 {
3722 FORWARD_TO_OUTER_OR_THROW(GetParent, (aError), aError, nullptr);
3723
3724 if (!mDocShell) {
3725 return nullptr;
3726 }
3727
3728 nsCOMPtr<nsIDOMWindow> parent;
3729 if (mDocShell->GetIsBrowserOrApp()) {
3730 parent = this;
3731 } else {
3732 aError = GetRealParent(getter_AddRefs(parent));
3733 }
3734
3735 return parent.forget();
3736 }
3737
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);
3751
3752 return rv.ErrorCode();
3753 }
3754
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);
3763
3764 *aParent = nullptr;
3765 if (!mDocShell) {
3766 return NS_OK;
3767 }
3768
3769 nsCOMPtr<nsIDocShell> parent;
3770 mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
3771
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 }
3783
3784 static nsresult
3785 GetTopImpl(nsGlobalWindow* aWin, nsIDOMWindow** aTop, bool aScriptable)
3786 {
3787 *aTop = nullptr;
3788
3789 // Walk up the parent chain.
3790
3791 nsCOMPtr<nsIDOMWindow> prevParent = aWin;
3792 nsCOMPtr<nsIDOMWindow> parent = aWin;
3793 do {
3794 if (!parent) {
3795 break;
3796 }
3797
3798 prevParent = parent;
3799
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);
3809
3810 parent = newParent;
3811
3812 } while (parent != prevParent);
3813
3814 if (parent) {
3815 parent.swap(*aTop);
3816 }
3817
3818 return NS_OK;
3819 }
3820
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 }
3834
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 }
3854
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, );
3861
3862 nsCOMPtr<nsIDOMWindow> content = GetContentInternal(aError);
3863 if (aError.Failed()) {
3864 return;
3865 }
3866
3867 if (content) {
3868 JS::Rooted<JS::Value> val(aCx);
3869 aError = nsContentUtils::WrapNative(aCx, content, &val);
3870 if (aError.Failed()) {
3871 return;
3872 }
3873
3874 aRetval.set(&val.toObject());
3875 return;
3876 }
3877
3878 if (!nsContentUtils::IsCallerChrome() || !IsChromeWindow()) {
3879 aError.Throw(NS_ERROR_FAILURE);
3880 return;
3881 }
3882
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 }
3889
3890 JS::Rooted<JS::Value> val(aCx, JS::NullValue());
3891 aError = treeOwner->GetContentWindow(aCx, &val);
3892 if (aError.Failed()) {
3893 return;
3894 }
3895
3896 aRetval.set(val.toObjectOrNull());
3897 }
3898
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 }
3908
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 }
3914
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));
3922
3923 if (baseWin) {
3924 bool visible = false;
3925 baseWin->GetVisibility(&visible);
3926
3927 if (!visible) {
3928 mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(primaryContent));
3929 }
3930 }
3931 }
3932
3933 if (!primaryContent) {
3934 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
3935 if (!treeOwner) {
3936 aError.Throw(NS_ERROR_FAILURE);
3937 return nullptr;
3938 }
3939
3940 treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent));
3941 }
3942
3943 domWindow = do_GetInterface(primaryContent);
3944 return domWindow.forget();
3945 }
3946
3947 NS_IMETHODIMP
3948 nsGlobalWindow::GetContent(nsIDOMWindow** aContent)
3949 {
3950 ErrorResult rv;
3951 *aContent = GetContentInternal(rv).take();
3952
3953 return rv.ErrorCode();
3954 }
3955
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 }
3965
3966 return rv.ErrorCode();
3967 }
3968
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 }
3980
3981 if (!mDocShell)
3982 return NS_ERROR_FAILURE;
3983
3984 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell));
3985 NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE);
3986
3987 NS_ADDREF(*aPrompt = prompter);
3988 return NS_OK;
3989 }
3990
3991 BarProp*
3992 nsGlobalWindow::GetMenubar(ErrorResult& aError)
3993 {
3994 FORWARD_TO_INNER_OR_THROW(GetMenubar, (aError), aError, nullptr);
3995
3996 if (!mMenubar) {
3997 mMenubar = new MenubarProp(this);
3998 }
3999
4000 return mMenubar;
4001 }
4002
4003 NS_IMETHODIMP
4004 nsGlobalWindow::GetMenubar(nsISupports** aMenubar)
4005 {
4006 ErrorResult rv;
4007 nsCOMPtr<nsISupports> menubar = GetMenubar(rv);
4008 menubar.forget(aMenubar);
4009
4010 return rv.ErrorCode();
4011 }
4012
4013 BarProp*
4014 nsGlobalWindow::GetToolbar(ErrorResult& aError)
4015 {
4016 FORWARD_TO_INNER_OR_THROW(GetToolbar, (aError), aError, nullptr);
4017
4018 if (!mToolbar) {
4019 mToolbar = new ToolbarProp(this);
4020 }
4021
4022 return mToolbar;
4023 }
4024
4025 NS_IMETHODIMP
4026 nsGlobalWindow::GetToolbar(nsISupports** aToolbar)
4027 {
4028 ErrorResult rv;
4029 nsCOMPtr<nsISupports> toolbar = GetToolbar(rv);
4030 toolbar.forget(aToolbar);
4031
4032 return rv.ErrorCode();
4033 }
4034
4035 BarProp*
4036 nsGlobalWindow::GetLocationbar(ErrorResult& aError)
4037 {
4038 FORWARD_TO_INNER_OR_THROW(GetLocationbar, (aError), aError, nullptr);
4039
4040 if (!mLocationbar) {
4041 mLocationbar = new LocationbarProp(this);
4042 }
4043 return mLocationbar;
4044 }
4045
4046 NS_IMETHODIMP
4047 nsGlobalWindow::GetLocationbar(nsISupports** aLocationbar)
4048 {
4049 ErrorResult rv;
4050 nsCOMPtr<nsISupports> locationbar = GetLocationbar(rv);
4051 locationbar.forget(aLocationbar);
4052
4053 return rv.ErrorCode();
4054 }
4055
4056 BarProp*
4057 nsGlobalWindow::GetPersonalbar(ErrorResult& aError)
4058 {
4059 FORWARD_TO_INNER_OR_THROW(GetPersonalbar, (aError), aError, nullptr);
4060
4061 if (!mPersonalbar) {
4062 mPersonalbar = new PersonalbarProp(this);
4063 }
4064 return mPersonalbar;
4065 }
4066
4067 NS_IMETHODIMP
4068 nsGlobalWindow::GetPersonalbar(nsISupports** aPersonalbar)
4069 {
4070 ErrorResult rv;
4071 nsCOMPtr<nsISupports> personalbar = GetPersonalbar(rv);
4072 personalbar.forget(aPersonalbar);
4073
4074 return rv.ErrorCode();
4075 }
4076
4077 BarProp*
4078 nsGlobalWindow::GetStatusbar(ErrorResult& aError)
4079 {
4080 FORWARD_TO_INNER_OR_THROW(GetStatusbar, (aError), aError, nullptr);
4081
4082 if (!mStatusbar) {
4083 mStatusbar = new StatusbarProp(this);
4084 }
4085 return mStatusbar;
4086 }
4087
4088 NS_IMETHODIMP
4089 nsGlobalWindow::GetStatusbar(nsISupports** aStatusbar)
4090 {
4091 ErrorResult rv;
4092 nsCOMPtr<nsISupports> statusbar = GetStatusbar(rv);
4093 statusbar.forget(aStatusbar);
4094
4095 return rv.ErrorCode();
4096 }
4097
4098 BarProp*
4099 nsGlobalWindow::GetScrollbars(ErrorResult& aError)
4100 {
4101 FORWARD_TO_INNER_OR_THROW(GetScrollbars, (aError), aError, nullptr);
4102
4103 if (!mScrollbars) {
4104 mScrollbars = new ScrollbarsProp(this);
4105 }
4106
4107 return mScrollbars;
4108 }
4109
4110 NS_IMETHODIMP
4111 nsGlobalWindow::GetScrollbars(nsISupports** aScrollbars)
4112 {
4113 ErrorResult rv;
4114 nsCOMPtr<nsISupports> scrollbars = GetScrollbars(rv);
4115 scrollbars.forget(aScrollbars);
4116
4117 return rv.ErrorCode();
4118 }
4119
4120 bool
4121 nsGlobalWindow::GetClosed(ErrorResult& aError)
4122 {
4123 FORWARD_TO_OUTER_OR_THROW(GetClosed, (aError), aError, false);
4124
4125 // If someone called close(), or if we don't have a docshell, we're closed.
4126 return mIsClosed || !mDocShell;
4127 }
4128
4129 NS_IMETHODIMP
4130 nsGlobalWindow::GetClosed(bool* aClosed)
4131 {
4132 ErrorResult rv;
4133 *aClosed = GetClosed(rv);
4134
4135 return rv.ErrorCode();
4136 }
4137
4138 nsDOMWindowList*
4139 nsGlobalWindow::GetWindowList()
4140 {
4141 MOZ_ASSERT(IsOuterWindow());
4142
4143 if (!mFrames && mDocShell) {
4144 mFrames = new nsDOMWindowList(mDocShell);
4145 }
4146
4147 return mFrames;
4148 }
4149
4150 NS_IMETHODIMP
4151 nsGlobalWindow::GetFrames(nsIDOMWindowCollection** aFrames)
4152 {
4153 FORWARD_TO_OUTER(GetFrames, (aFrames), NS_ERROR_NOT_INITIALIZED);
4154
4155 *aFrames = GetWindowList();
4156 NS_IF_ADDREF(*aFrames);
4157 return NS_OK;
4158 }
4159
4160 already_AddRefed<nsIDOMWindow>
4161 nsGlobalWindow::IndexedGetter(uint32_t aIndex, bool& aFound)
4162 {
4163 aFound = false;
4164
4165 FORWARD_TO_OUTER(IndexedGetter, (aIndex, aFound), nullptr);
4166
4167 nsDOMWindowList* windows = GetWindowList();
4168 NS_ENSURE_TRUE(windows, nullptr);
4169
4170 return windows->IndexedGetter(aIndex, aFound);
4171 }
4172
4173 void
4174 nsGlobalWindow::GetSupportedNames(nsTArray<nsString>& aNames)
4175 {
4176 FORWARD_TO_OUTER_VOID(GetSupportedNames, (aNames));
4177
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 }
4189
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());
4196
4197 if (!JSID_IS_STRING(aId)) {
4198 return true;
4199 }
4200
4201 nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc);
4202 if (NS_FAILED(rv)) {
4203 return Throw(aCx, rv);
4204 }
4205
4206 return true;
4207 }
4208
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 }
4219
4220 JSContext* mCx;
4221 nsGlobalWindow* mWindow;
4222 JS::Rooted<JSObject*> mWrapper;
4223 nsTArray<nsString>& mNames;
4224 };
4225
4226 static PLDHashOperator
4227 EnumerateGlobalName(const nsAString& aName,
4228 const nsGlobalNameStruct& aNameStruct,
4229 void* aClosure)
4230 {
4231 GlobalNameEnumeratorClosure* closure =
4232 static_cast<GlobalNameEnumeratorClosure*>(aClosure);
4233
4234 if (aNameStruct.mType == nsGlobalNameStruct::eTypeStaticNameSet) {
4235 // We have no idea what names this might install.
4236 return PL_DHASH_NEXT;
4237 }
4238
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 }
4247
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"));
4254
4255 nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
4256 if (nameSpaceManager) {
4257 GlobalNameEnumeratorClosure closure(aCx, this, aNames);
4258 nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &closure);
4259 }
4260 }
4261
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 }
4268
4269 nsIDOMOfflineResourceList*
4270 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
4271 {
4272 FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr);
4273
4274 if (!mApplicationCache) {
4275 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
4276 if (!webNav) {
4277 aError.Throw(NS_ERROR_FAILURE);
4278 return nullptr;
4279 }
4280
4281 nsCOMPtr<nsIURI> uri;
4282 aError = webNav->GetCurrentURI(getter_AddRefs(uri));
4283 if (aError.Failed()) {
4284 return nullptr;
4285 }
4286
4287 nsCOMPtr<nsIURI> manifestURI;
4288 nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI));
4289
4290 nsRefPtr<nsDOMOfflineResourceList> applicationCache =
4291 new nsDOMOfflineResourceList(manifestURI, uri, this);
4292
4293 applicationCache->Init();
4294
4295 mApplicationCache = applicationCache;
4296 }
4297
4298 return mApplicationCache;
4299 }
4300
4301 NS_IMETHODIMP
4302 nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCache)
4303 {
4304 ErrorResult rv;
4305 nsCOMPtr<nsIDOMOfflineResourceList> applicationCache =
4306 GetApplicationCache(rv);
4307 applicationCache.forget(aApplicationCache);
4308
4309 return rv.ErrorCode();
4310 }
4311
4312 nsIDOMCrypto*
4313 nsGlobalWindow::GetCrypto(ErrorResult& aError)
4314 {
4315 FORWARD_TO_INNER_OR_THROW(GetCrypto, (aError), aError, nullptr);
4316
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 }
4331
4332 mCrypto->Init(this);
4333 }
4334 return mCrypto;
4335 }
4336
4337 NS_IMETHODIMP
4338 nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto)
4339 {
4340 ErrorResult rv;
4341 nsCOMPtr<nsIDOMCrypto> crypto = GetCrypto(rv);
4342 crypto.forget(aCrypto);
4343
4344 return rv.ErrorCode();
4345 }
4346
4347 nsIControllers*
4348 nsGlobalWindow::GetControllers(ErrorResult& aError)
4349 {
4350 FORWARD_TO_OUTER_OR_THROW(GetControllers, (aError), aError, nullptr);
4351
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 }
4359
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 }
4367
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 }
4374
4375 controllerContext->SetCommandContext(static_cast<nsIDOMWindow*>(this));
4376 }
4377
4378 return mControllers;
4379 }
4380
4381 NS_IMETHODIMP
4382 nsGlobalWindow::GetControllers(nsIControllers** aResult)
4383 {
4384 ErrorResult rv;
4385 nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
4386 controllers.forget(aResult);
4387
4388 return rv.ErrorCode();
4389 }
4390
4391 nsIDOMWindow*
4392 nsGlobalWindow::GetOpenerWindow(ErrorResult& aError)
4393 {
4394 FORWARD_TO_OUTER_OR_THROW(GetOpenerWindow, (aError), aError, nullptr);
4395
4396 nsCOMPtr<nsPIDOMWindow> opener = do_QueryReferent(mOpener);
4397 if (!opener) {
4398 return nullptr;
4399 }
4400
4401 // First, check if we were called from a privileged chrome script
4402 if (nsContentUtils::IsCallerChrome()) {
4403 return opener;
4404 }
4405
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 }
4411
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();
4416
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 }
4429
4430 return nullptr;
4431 }
4432
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 }
4442
4443 aError = nsContentUtils::WrapNative(aCx, opener, aRetval);
4444 }
4445
4446 NS_IMETHODIMP
4447 nsGlobalWindow::GetScriptableOpener(JSContext* aCx,
4448 JS::MutableHandle<JS::Value> aOpener)
4449 {
4450 ErrorResult rv;
4451 GetOpener(aCx, aOpener, rv);
4452
4453 return rv.ErrorCode();
4454 }
4455
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 }
4464
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 }
4481
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 }
4487
4488 return;
4489 }
4490
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 }
4496
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 }
4505
4506 win = xpc::WindowOrNull(unwrapped);
4507 if (!win) {
4508 // Wasn't a window
4509 aError.Throw(NS_ERROR_INVALID_ARG);
4510 return;
4511 }
4512 }
4513
4514 SetOpenerWindow(win, false);
4515 }
4516
4517 NS_IMETHODIMP
4518 nsGlobalWindow::SetScriptableOpener(JSContext* aCx,
4519 JS::Handle<JS::Value> aOpener)
4520 {
4521 ErrorResult rv;
4522 SetOpener(aCx, aOpener, rv);
4523
4524 return rv.ErrorCode();
4525 }
4526
4527 NS_IMETHODIMP
4528 nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener)
4529 {
4530 SetOpenerWindow(aOpener, false);
4531 return NS_OK;
4532 }
4533
4534 void
4535 nsGlobalWindow::GetStatus(nsAString& aStatus, ErrorResult& aError)
4536 {
4537 FORWARD_TO_OUTER_OR_THROW(GetStatus, (aStatus, aError), aError, );
4538
4539 aStatus = mStatus;
4540 }
4541
4542 NS_IMETHODIMP
4543 nsGlobalWindow::GetStatus(nsAString& aStatus)
4544 {
4545 ErrorResult rv;
4546 GetStatus(aStatus, rv);
4547
4548 return rv.ErrorCode();
4549 }
4550
4551 void
4552 nsGlobalWindow::SetStatus(const nsAString& aStatus, ErrorResult& aError)
4553 {
4554 FORWARD_TO_OUTER_OR_THROW(SetStatus, (aStatus, aError), aError, );
4555
4556 mStatus = aStatus;
4557
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 */
4562
4563 if (!CanSetProperty("dom.disable_window_status_change")) {
4564 return;
4565 }
4566
4567 nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome();
4568 if (browserChrome) {
4569 browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
4570 PromiseFlatString(aStatus).get());
4571 }
4572 }
4573
4574 NS_IMETHODIMP
4575 nsGlobalWindow::SetStatus(const nsAString& aStatus)
4576 {
4577 ErrorResult rv;
4578 SetStatus(aStatus, rv);
4579
4580 return rv.ErrorCode();
4581 }
4582
4583 void
4584 nsGlobalWindow::GetName(nsAString& aName, ErrorResult& aError)
4585 {
4586 FORWARD_TO_OUTER_OR_THROW(GetName, (aName, aError), aError, );
4587
4588 if (mDocShell) {
4589 mDocShell->GetName(aName);
4590 }
4591 }
4592
4593 NS_IMETHODIMP
4594 nsGlobalWindow::GetName(nsAString& aName)
4595 {
4596 ErrorResult rv;
4597 GetName(aName, rv);
4598
4599 return rv.ErrorCode();
4600 }
4601
4602 void
4603 nsGlobalWindow::SetName(const nsAString& aName, mozilla::ErrorResult& aError)
4604 {
4605 FORWARD_TO_OUTER_OR_THROW(SetName, (aName, aError), aError, );
4606
4607 if (mDocShell) {
4608 aError = mDocShell->SetName(aName);
4609 }
4610 }
4611
4612 NS_IMETHODIMP
4613 nsGlobalWindow::SetName(const nsAString& aName)
4614 {
4615 ErrorResult rv;
4616 SetName(aName, rv);
4617
4618 return rv.ErrorCode();
4619 }
4620
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
4627
4628 nsRefPtr<nsPresContext> presContext;
4629 mDocShell->GetPresContext(getter_AddRefs(presContext));
4630 if (!presContext)
4631 return px;
4632
4633 return presContext->DevPixelsToIntCSSPixels(px);
4634 }
4635
4636 int32_t
4637 nsGlobalWindow::CSSToDevIntPixels(int32_t px)
4638 {
4639 if (!mDocShell)
4640 return px; // assume 1:1
4641
4642 nsRefPtr<nsPresContext> presContext;
4643 mDocShell->GetPresContext(getter_AddRefs(presContext));
4644 if (!presContext)
4645 return px;
4646
4647 return presContext->CSSPixelsToDevPixels(px);
4648 }
4649
4650 nsIntSize
4651 nsGlobalWindow::DevToCSSIntPixels(nsIntSize px)
4652 {
4653 if (!mDocShell)
4654 return px; // assume 1:1
4655
4656 nsRefPtr<nsPresContext> presContext;
4657 mDocShell->GetPresContext(getter_AddRefs(presContext));
4658 if (!presContext)
4659 return px;
4660
4661 return nsIntSize(
4662 presContext->DevPixelsToIntCSSPixels(px.width),
4663 presContext->DevPixelsToIntCSSPixels(px.height));
4664 }
4665
4666 nsIntSize
4667 nsGlobalWindow::CSSToDevIntPixels(nsIntSize px)
4668 {
4669 if (!mDocShell)
4670 return px; // assume 1:1
4671
4672 nsRefPtr<nsPresContext> presContext;
4673 mDocShell->GetPresContext(getter_AddRefs(presContext));
4674 if (!presContext)
4675 return px;
4676
4677 return nsIntSize(
4678 presContext->CSSPixelsToDevPixels(px.width),
4679 presContext->CSSPixelsToDevPixels(px.height));
4680 }
4681
4682 nsresult
4683 nsGlobalWindow::GetInnerSize(CSSIntSize& aSize)
4684 {
4685 MOZ_ASSERT(IsOuterWindow());
4686
4687 EnsureSizeUpToDate();
4688
4689 NS_ENSURE_STATE(mDocShell);
4690
4691 nsRefPtr<nsPresContext> presContext;
4692 mDocShell->GetPresContext(getter_AddRefs(presContext));
4693 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4694
4695 if (!presContext || !presShell) {
4696 aSize = CSSIntSize(0, 0);
4697 return NS_OK;
4698 }
4699
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 }
4714
4715 aSize = CSSIntRect::FromAppUnitsRounded(
4716 presContext->GetVisibleArea().Size());
4717 }
4718 return NS_OK;
4719 }
4720
4721 int32_t
4722 nsGlobalWindow::GetInnerWidth(ErrorResult& aError)
4723 {
4724 FORWARD_TO_OUTER_OR_THROW(GetInnerWidth, (aError), aError, 0);
4725
4726 CSSIntSize size;
4727 aError = GetInnerSize(size);
4728 return size.width;
4729 }
4730
4731 NS_IMETHODIMP
4732 nsGlobalWindow::GetInnerWidth(int32_t* aInnerWidth)
4733 {
4734 ErrorResult rv;
4735 *aInnerWidth = GetInnerWidth(rv);
4736
4737 return rv.ErrorCode();
4738 }
4739
4740 void
4741 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth, ErrorResult& aError)
4742 {
4743 FORWARD_TO_OUTER_OR_THROW(SetInnerWidth, (aInnerWidth, aError), aError, );
4744
4745 if (!mDocShell) {
4746 aError.Throw(NS_ERROR_UNEXPECTED);
4747 return;
4748 }
4749
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 }
4757
4758 CheckSecurityWidthAndHeight(&aInnerWidth, nullptr);
4759
4760 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4761
4762 if (presShell && presShell->GetIsViewportOverridden())
4763 {
4764 nscoord height = 0;
4765
4766 nsRefPtr<nsPresContext> presContext;
4767 presContext = presShell->GetPresContext();
4768
4769 nsRect shellArea = presContext->GetVisibleArea();
4770 height = shellArea.height;
4771 SetCSSViewportWidthAndHeight(nsPresContext::CSSPixelsToAppUnits(aInnerWidth),
4772 height);
4773 return;
4774 }
4775
4776 int32_t height = 0;
4777 int32_t unused = 0;
4778
4779 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
4780 docShellAsWin->GetSize(&unused, &height);
4781 aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height);
4782 }
4783
4784 NS_IMETHODIMP
4785 nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth)
4786 {
4787 ErrorResult rv;
4788 SetInnerWidth(aInnerWidth, rv);
4789
4790 return rv.ErrorCode();
4791 }
4792
4793 int32_t
4794 nsGlobalWindow::GetInnerHeight(ErrorResult& aError)
4795 {
4796 FORWARD_TO_OUTER_OR_THROW(GetInnerHeight, (aError), aError, 0);
4797
4798 CSSIntSize size;
4799 aError = GetInnerSize(size);
4800 return size.height;
4801 }
4802
4803 NS_IMETHODIMP
4804 nsGlobalWindow::GetInnerHeight(int32_t* aInnerHeight)
4805 {
4806 ErrorResult rv;
4807 *aInnerHeight = GetInnerHeight(rv);
4808
4809 return rv.ErrorCode();
4810 }
4811
4812 void
4813 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight, ErrorResult& aError)
4814 {
4815 FORWARD_TO_OUTER_OR_THROW(SetInnerHeight, (aInnerHeight, aError), aError, );
4816
4817 if (!mDocShell) {
4818 aError.Throw(NS_ERROR_UNEXPECTED);
4819 return;
4820 }
4821
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 }
4829
4830 nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
4831
4832 if (presShell && presShell->GetIsViewportOverridden())
4833 {
4834 nsRefPtr<nsPresContext> presContext;
4835 presContext = presShell->GetPresContext();
4836
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 }
4845
4846 int32_t height = 0;
4847 int32_t width = 0;
4848
4849 nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
4850 docShellAsWin->GetSize(&width, &height);
4851 CheckSecurityWidthAndHeight(nullptr, &aInnerHeight);
4852 aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
4853 }
4854
4855 NS_IMETHODIMP
4856 nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight)
4857 {
4858 ErrorResult rv;
4859 SetInnerHeight(aInnerHeight, rv);
4860
4861 return rv.ErrorCode();
4862 }
4863
4864 nsIntSize
4865 nsGlobalWindow::GetOuterSize(ErrorResult& aError)
4866 {
4867 MOZ_ASSERT(IsOuterWindow());
4868
4869 if (!IsChrome()) {
4870 CSSIntSize size;
4871 aError = GetInnerSize(size);
4872 return nsIntSize(size.width, size.height);
4873 }
4874
4875 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4876 if (!treeOwnerAsWin) {
4877 aError.Throw(NS_ERROR_FAILURE);
4878 return nsIntSize(0, 0);
4879 }
4880
4881 nsGlobalWindow* rootWindow =
4882 static_cast<nsGlobalWindow *>(GetPrivateRoot());
4883 if (rootWindow) {
4884 rootWindow->FlushPendingNotifications(Flush_Layout);
4885 }
4886
4887 nsIntSize sizeDevPixels;
4888 aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
4889 if (aError.Failed()) {
4890 return nsIntSize();
4891 }
4892
4893 return DevToCSSIntPixels(sizeDevPixels);
4894 }
4895
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 }
4902
4903 NS_IMETHODIMP
4904 nsGlobalWindow::GetOuterWidth(int32_t* aOuterWidth)
4905 {
4906 ErrorResult rv;
4907 *aOuterWidth = GetOuterWidth(rv);
4908
4909 return rv.ErrorCode();
4910 }
4911
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 }
4918
4919 NS_IMETHODIMP
4920 nsGlobalWindow::GetOuterHeight(int32_t* aOuterHeight)
4921 {
4922 ErrorResult rv;
4923 *aOuterHeight = GetOuterHeight(rv);
4924
4925 return rv.ErrorCode();
4926 }
4927
4928 void
4929 nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
4930 ErrorResult& aError)
4931 {
4932 MOZ_ASSERT(IsOuterWindow());
4933
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 */
4938
4939 if (!CanMoveResizeWindows() || IsFrame()) {
4940 return;
4941 }
4942
4943 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
4944 if (!treeOwnerAsWin) {
4945 aError.Throw(NS_ERROR_FAILURE);
4946 return;
4947 }
4948
4949 CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
4950 aIsWidth ? nullptr : &aLengthCSSPixels);
4951
4952 int32_t width, height;
4953 aError = treeOwnerAsWin->GetSize(&width, &height);
4954 if (aError.Failed()) {
4955 return;
4956 }
4957
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 }
4966
4967 void
4968 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth, ErrorResult& aError)
4969 {
4970 FORWARD_TO_OUTER_OR_THROW(SetOuterWidth, (aOuterWidth, aError), aError, );
4971
4972 SetOuterSize(aOuterWidth, true, aError);
4973 }
4974
4975 NS_IMETHODIMP
4976 nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth)
4977 {
4978 ErrorResult rv;
4979 SetOuterWidth(aOuterWidth, rv);
4980
4981 return rv.ErrorCode();
4982 }
4983
4984 void
4985 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight, ErrorResult& aError)
4986 {
4987 FORWARD_TO_OUTER_OR_THROW(SetOuterHeight, (aOuterHeight, aError), aError, );
4988
4989 SetOuterSize(aOuterHeight, false, aError);
4990 }
4991
4992 NS_IMETHODIMP
4993 nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight)
4994 {
4995 ErrorResult rv;
4996 SetOuterHeight(aOuterHeight, rv);
4997
4998 return rv.ErrorCode();
4999 }
5000
5001 nsIntPoint
5002 nsGlobalWindow::GetScreenXY(ErrorResult& aError)
5003 {
5004 MOZ_ASSERT(IsOuterWindow());
5005
5006 // For non-chrome callers, always return (0,0) to prevent fingerprinting.
5007 if (!IsChrome()) {
5008 return nsIntPoint(0, 0);
5009 }
5010
5011 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5012 if (!treeOwnerAsWin) {
5013 aError.Throw(NS_ERROR_FAILURE);
5014 return nsIntPoint(0, 0);
5015 }
5016
5017 int32_t x = 0, y = 0;
5018 aError = treeOwnerAsWin->GetPosition(&x, &y);
5019 return nsIntPoint(x, y);
5020 }
5021
5022 int32_t
5023 nsGlobalWindow::GetScreenX(ErrorResult& aError)
5024 {
5025 FORWARD_TO_OUTER_OR_THROW(GetScreenX, (aError), aError, 0);
5026
5027 return DevToCSSIntPixels(GetScreenXY(aError).x);
5028 }
5029
5030 NS_IMETHODIMP
5031 nsGlobalWindow::GetScreenX(int32_t* aScreenX)
5032 {
5033 ErrorResult rv;
5034 *aScreenX = GetScreenX(rv);
5035
5036 return rv.ErrorCode();
5037 }
5038
5039 nsRect
5040 nsGlobalWindow::GetInnerScreenRect()
5041 {
5042 MOZ_ASSERT(IsOuterWindow());
5043
5044 if (!mDocShell) {
5045 return nsRect();
5046 }
5047
5048 nsGlobalWindow* rootWindow =
5049 static_cast<nsGlobalWindow*>(GetPrivateRoot());
5050 if (rootWindow) {
5051 rootWindow->FlushPendingNotifications(Flush_Layout);
5052 }
5053
5054 if (!mDocShell) {
5055 return nsRect();
5056 }
5057
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 }
5066
5067 return rootFrame->GetScreenRectInAppUnits();
5068 }
5069
5070 float
5071 nsGlobalWindow::GetMozInnerScreenX(ErrorResult& aError)
5072 {
5073 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenX, (aError), aError, 0);
5074
5075 // For non-chrome callers, always return 0 to prevent fingerprinting.
5076 if (!IsChrome()) return 0.0;
5077
5078 nsRect r = GetInnerScreenRect();
5079 return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
5080 }
5081
5082 NS_IMETHODIMP
5083 nsGlobalWindow::GetMozInnerScreenX(float* aScreenX)
5084 {
5085 ErrorResult rv;
5086 *aScreenX = GetMozInnerScreenX(rv);
5087
5088 return rv.ErrorCode();
5089 }
5090
5091 float
5092 nsGlobalWindow::GetMozInnerScreenY(ErrorResult& aError)
5093 {
5094 FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenY, (aError), aError, 0);
5095
5096 // For non-chrome callers, always return 0 to prevent fingerprinting.
5097 if (!IsChrome()) return 0.0;
5098
5099 nsRect r = GetInnerScreenRect();
5100 return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
5101 }
5102
5103 NS_IMETHODIMP
5104 nsGlobalWindow::GetMozInnerScreenY(float* aScreenY)
5105 {
5106 ErrorResult rv;
5107 *aScreenY = GetMozInnerScreenY(rv);
5108
5109 return rv.ErrorCode();
5110 }
5111
5112 float
5113 nsGlobalWindow::GetDevicePixelRatio(ErrorResult& aError)
5114 {
5115 FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatio, (aError), aError, 0.0);
5116
5117 if (!mDocShell) {
5118 return 1.0;
5119 }
5120
5121 nsRefPtr<nsPresContext> presContext;
5122 mDocShell->GetPresContext(getter_AddRefs(presContext));
5123 if (!presContext) {
5124 return 1.0;
5125 }
5126
5127 return float(nsPresContext::AppUnitsPerCSSPixel())/
5128 presContext->AppUnitsPerDevPixel();
5129 }
5130
5131 NS_IMETHODIMP
5132 nsGlobalWindow::GetDevicePixelRatio(float* aRatio)
5133 {
5134 ErrorResult rv;
5135 *aRatio = GetDevicePixelRatio(rv);
5136
5137 return rv.ErrorCode();
5138 }
5139
5140 uint64_t
5141 nsGlobalWindow::GetMozPaintCount(ErrorResult& aError)
5142 {
5143 FORWARD_TO_OUTER_OR_THROW(GetMozPaintCount, (aError), aError, 0);
5144
5145 if (!mDocShell) {
5146 return 0;
5147 }
5148
5149 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
5150 return presShell ? presShell->GetPaintCount() : 0;
5151 }
5152
5153 NS_IMETHODIMP
5154 nsGlobalWindow::GetMozPaintCount(uint64_t* aResult)
5155 {
5156 ErrorResult rv;
5157 *aResult = GetMozPaintCount(rv);
5158
5159 return rv.ErrorCode();
5160 }
5161
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 }
5172
5173 ErrorResult rv;
5174 nsIDocument::FrameRequestCallbackHolder holder(aCallback);
5175 *aHandle = RequestAnimationFrame(holder, rv);
5176
5177 return rv.ErrorCode();
5178 }
5179
5180 int32_t
5181 nsGlobalWindow::RequestAnimationFrame(FrameRequestCallback& aCallback,
5182 ErrorResult& aError)
5183 {
5184 nsIDocument::FrameRequestCallbackHolder holder(&aCallback);
5185 return RequestAnimationFrame(holder, aError);
5186 }
5187
5188 int32_t
5189 nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback,
5190 ErrorResult& aError)
5191 {
5192 nsIDocument::FrameRequestCallbackHolder holder(aCallback);
5193 return RequestAnimationFrame(holder, aError);
5194 }
5195
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);
5202
5203 if (!mDoc) {
5204 return 0;
5205 }
5206
5207 if (GetWrapperPreserveColor()) {
5208 js::NotifyAnimationActivity(GetWrapperPreserveColor());
5209 }
5210
5211 int32_t handle;
5212 aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
5213 return handle;
5214 }
5215
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 }
5224
5225 JS::Rooted<JSObject*> callbackObj(cx, &aCallback.toObject());
5226 nsRefPtr<FrameRequestCallback> callback =
5227 new FrameRequestCallback(callbackObj, GetIncumbentGlobal());
5228
5229 ErrorResult rv;
5230 *aHandle = RequestAnimationFrame(*callback, rv);
5231
5232 return rv.ErrorCode();
5233 }
5234
5235 NS_IMETHODIMP
5236 nsGlobalWindow::MozCancelRequestAnimationFrame(int32_t aHandle)
5237 {
5238 return CancelAnimationFrame(aHandle);
5239 }
5240
5241 NS_IMETHODIMP
5242 nsGlobalWindow::MozCancelAnimationFrame(int32_t aHandle)
5243 {
5244 return CancelAnimationFrame(aHandle);
5245 }
5246
5247 void
5248 nsGlobalWindow::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError)
5249 {
5250 FORWARD_TO_INNER_OR_THROW(CancelAnimationFrame, (aHandle, aError), aError, );
5251
5252 if (!mDoc) {
5253 return;
5254 }
5255
5256 mDoc->CancelFrameRequestCallback(aHandle);
5257 }
5258
5259 NS_IMETHODIMP
5260 nsGlobalWindow::CancelAnimationFrame(int32_t aHandle)
5261 {
5262 ErrorResult rv;
5263 CancelAnimationFrame(aHandle, rv);
5264
5265 return rv.ErrorCode();
5266 }
5267
5268 int64_t
5269 nsGlobalWindow::GetMozAnimationStartTime(ErrorResult& aError)
5270 {
5271 FORWARD_TO_INNER_OR_THROW(GetMozAnimationStartTime, (aError), aError, 0);
5272
5273 if (mDoc) {
5274 nsIPresShell* presShell = mDoc->GetShell();
5275 if (presShell) {
5276 return presShell->GetPresContext()->RefreshDriver()->
5277 MostRecentRefreshEpochTime() / PR_USEC_PER_MSEC;
5278 }
5279 }
5280
5281 // If all else fails, just be compatible with Date.now()
5282 return JS_Now() / PR_USEC_PER_MSEC;
5283 }
5284
5285 NS_IMETHODIMP
5286 nsGlobalWindow::GetMozAnimationStartTime(int64_t *aTime)
5287 {
5288 ErrorResult rv;
5289 *aTime = GetMozAnimationStartTime(rv);
5290
5291 return rv.ErrorCode();
5292 }
5293
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);
5304
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 }
5312
5313 if (!mDocShell) {
5314 return nullptr;
5315 }
5316
5317 nsRefPtr<nsPresContext> presContext;
5318 mDocShell->GetPresContext(getter_AddRefs(presContext));
5319
5320 if (!presContext) {
5321 return nullptr;
5322 }
5323
5324 return presContext->MatchMedia(aMediaQueryList);
5325 }
5326
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);
5334
5335 return rv.ErrorCode();
5336 }
5337
5338 void
5339 nsGlobalWindow::SetScreenX(int32_t aScreenX, ErrorResult& aError)
5340 {
5341 FORWARD_TO_OUTER_OR_THROW(SetScreenX, (aScreenX, aError), aError, );
5342
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 */
5347
5348 if (!CanMoveResizeWindows() || IsFrame()) {
5349 return;
5350 }
5351
5352 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5353 if (!treeOwnerAsWin) {
5354 aError.Throw(NS_ERROR_FAILURE);
5355 return;
5356 }
5357
5358 int32_t x, y;
5359 aError = treeOwnerAsWin->GetPosition(&x, &y);
5360 if (aError.Failed()) {
5361 return;
5362 }
5363
5364 CheckSecurityLeftAndTop(&aScreenX, nullptr);
5365 x = CSSToDevIntPixels(aScreenX);
5366
5367 aError = treeOwnerAsWin->SetPosition(x, y);
5368 }
5369
5370 NS_IMETHODIMP
5371 nsGlobalWindow::SetScreenX(int32_t aScreenX)
5372 {
5373 ErrorResult rv;
5374 SetScreenX(aScreenX, rv);
5375
5376 return rv.ErrorCode();
5377 }
5378
5379 int32_t
5380 nsGlobalWindow::GetScreenY(ErrorResult& aError)
5381 {
5382 FORWARD_TO_OUTER_OR_THROW(GetScreenY, (aError), aError, 0);
5383
5384 return DevToCSSIntPixels(GetScreenXY(aError).y);
5385 }
5386
5387 NS_IMETHODIMP
5388 nsGlobalWindow::GetScreenY(int32_t* aScreenY)
5389 {
5390 ErrorResult rv;
5391 *aScreenY = GetScreenY(rv);
5392
5393 return rv.ErrorCode();
5394 }
5395
5396 void
5397 nsGlobalWindow::SetScreenY(int32_t aScreenY, ErrorResult& aError)
5398 {
5399 FORWARD_TO_OUTER_OR_THROW(SetScreenY, (aScreenY, aError), aError, );
5400
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 */
5405
5406 if (!CanMoveResizeWindows() || IsFrame()) {
5407 return;
5408 }
5409
5410 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5411 if (!treeOwnerAsWin) {
5412 aError.Throw(NS_ERROR_FAILURE);
5413 return;
5414 }
5415
5416 int32_t x, y;
5417 aError = treeOwnerAsWin->GetPosition(&x, &y);
5418 if (aError.Failed()) {
5419 return;
5420 }
5421
5422 CheckSecurityLeftAndTop(nullptr, &aScreenY);
5423 y = CSSToDevIntPixels(aScreenY);
5424
5425 aError = treeOwnerAsWin->SetPosition(x, y);
5426 }
5427
5428 NS_IMETHODIMP
5429 nsGlobalWindow::SetScreenY(int32_t aScreenY)
5430 {
5431 ErrorResult rv;
5432 SetScreenY(aScreenY, rv);
5433
5434 return rv.ErrorCode();
5435 }
5436
5437 bool
5438 nsGlobalWindow::IsChrome() const
5439 {
5440 bool isChrome = false;
5441
5442 if (mDocShell) {
5443 nsRefPtr<nsPresContext> presContext;
5444 mDocShell->GetPresContext(getter_AddRefs(presContext));
5445 isChrome = (presContext && presContext->IsChrome());
5446 }
5447
5448 return isChrome;
5449 }
5450
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());
5457
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
5464
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
5468
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 }
5480
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());
5486
5487 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
5488
5489 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
5490 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
5491 NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
5492
5493 NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(mDocShell, aInnerWidth, aInnerHeight),
5494 NS_ERROR_FAILURE);
5495
5496 return NS_OK;
5497 }
5498
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());
5504
5505 nsRefPtr<nsPresContext> presContext;
5506 mDocShell->GetPresContext(getter_AddRefs(presContext));
5507
5508 nsRect shellArea = presContext->GetVisibleArea();
5509 shellArea.height = aInnerHeight;
5510 shellArea.width = aInnerWidth;
5511
5512 presContext->SetVisibleArea(shellArea);
5513 }
5514
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());
5521
5522 // This one is harder. We have to get the screen size and window dimensions.
5523
5524 // Check security state for use in determing window dimensions
5525
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
5531
5532 nsGlobalWindow* rootWindow =
5533 static_cast<nsGlobalWindow*>(GetPrivateRoot());
5534 if (rootWindow) {
5535 rootWindow->FlushPendingNotifications(Flush_Layout);
5536 }
5537
5538 nsCOMPtr<nsIBaseWindow> treeOwner = GetTreeOwnerWindow();
5539
5540 nsCOMPtr<nsIDOMScreen> screen;
5541 GetScreen(getter_AddRefs(screen));
5542
5543 if (treeOwner && screen) {
5544 int32_t screenLeft, screenTop, screenWidth, screenHeight;
5545 int32_t winLeft, winTop, winWidth, winHeight;
5546
5547 // Get the window size
5548 treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
5549
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);
5556
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
5574
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 }
5595
5596 NS_IMETHODIMP
5597 nsGlobalWindow::GetPageXOffset(int32_t* aPageXOffset)
5598 {
5599 return GetScrollX(aPageXOffset);
5600 }
5601
5602 NS_IMETHODIMP
5603 nsGlobalWindow::GetPageYOffset(int32_t* aPageYOffset)
5604 {
5605 return GetScrollY(aPageYOffset);
5606 }
5607
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, );
5614
5615 FlushPendingNotifications(Flush_Layout);
5616 nsIScrollableFrame *sf = GetScrollFrame();
5617 if (!sf) {
5618 return;
5619 }
5620
5621 nsRect scrollRange = sf->GetScrollRange();
5622
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 }
5632
5633 int32_t
5634 nsGlobalWindow::GetScrollMaxX(ErrorResult& aError)
5635 {
5636 int32_t scrollMaxX = 0;
5637 GetScrollMaxXY(&scrollMaxX, nullptr, aError);
5638 return scrollMaxX;
5639 }
5640
5641 NS_IMETHODIMP
5642 nsGlobalWindow::GetScrollMaxX(int32_t* aScrollMaxX)
5643 {
5644 NS_ENSURE_ARG_POINTER(aScrollMaxX);
5645 ErrorResult rv;
5646 *aScrollMaxX = GetScrollMaxX(rv);
5647
5648 return rv.ErrorCode();
5649 }
5650
5651 int32_t
5652 nsGlobalWindow::GetScrollMaxY(ErrorResult& aError)
5653 {
5654 int32_t scrollMaxY = 0;
5655 GetScrollMaxXY(nullptr, &scrollMaxY, aError);
5656 return scrollMaxY;
5657 }
5658
5659 NS_IMETHODIMP
5660 nsGlobalWindow::GetScrollMaxY(int32_t* aScrollMaxY)
5661 {
5662 NS_ENSURE_ARG_POINTER(aScrollMaxY);
5663 ErrorResult rv;
5664 *aScrollMaxY = GetScrollMaxY(rv);
5665
5666 return rv.ErrorCode();
5667 }
5668
5669 CSSIntPoint
5670 nsGlobalWindow::GetScrollXY(bool aDoFlush, ErrorResult& aError)
5671 {
5672 FORWARD_TO_OUTER_OR_THROW(GetScrollXY, (aDoFlush, aError), aError,
5673 CSSIntPoint(0, 0));
5674
5675 if (aDoFlush) {
5676 FlushPendingNotifications(Flush_Layout);
5677 } else {
5678 EnsureSizeUpToDate();
5679 }
5680
5681 nsIScrollableFrame *sf = GetScrollFrame();
5682 if (!sf) {
5683 return CSSIntPoint(0, 0);
5684 }
5685
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 }
5693
5694 return sf->GetScrollPositionCSSPixels();
5695 }
5696
5697 int32_t
5698 nsGlobalWindow::GetScrollX(ErrorResult& aError)
5699 {
5700 return GetScrollXY(false, aError).x;
5701 }
5702
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 }
5711
5712 int32_t
5713 nsGlobalWindow::GetScrollY(ErrorResult& aError)
5714 {
5715 return GetScrollXY(false, aError).y;
5716 }
5717
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 }
5726
5727 uint32_t
5728 nsGlobalWindow::Length()
5729 {
5730 FORWARD_TO_OUTER(Length, (), 0);
5731
5732 nsDOMWindowList* windows = GetWindowList();
5733
5734 return windows ? windows->GetLength() : 0;
5735 }
5736
5737 NS_IMETHODIMP
5738 nsGlobalWindow::GetLength(uint32_t* aLength)
5739 {
5740 *aLength = Length();
5741 return NS_OK;
5742 }
5743
5744 already_AddRefed<nsIDOMWindow>
5745 nsGlobalWindow::GetChildWindow(const nsAString& aName)
5746 {
5747 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
5748 NS_ENSURE_TRUE(docShell, nullptr);
5749
5750 nsCOMPtr<nsIDocShellTreeItem> child;
5751 docShell->FindChildWithName(PromiseFlatString(aName).get(),
5752 false, true, nullptr, nullptr,
5753 getter_AddRefs(child));
5754
5755 nsCOMPtr<nsIDOMWindow> child_win(do_GetInterface(child));
5756 return child_win.forget();
5757 }
5758
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);
5767
5768 return defaultActionEnabled;
5769 }
5770
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 }
5781
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 }
5791
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 }
5802
5803 domEvent->SetTrusted(true);
5804 domEvent->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
5805
5806 nsCOMPtr<EventTarget> target = do_QueryInterface(GetOuterWindow());
5807 domEvent->SetTarget(target);
5808
5809 bool defaultActionEnabled = true;
5810 target->DispatchEvent(domEvent, &defaultActionEnabled);
5811
5812 return defaultActionEnabled;
5813 }
5814
5815 void
5816 nsGlobalWindow::RefreshCompartmentPrincipal()
5817 {
5818 FORWARD_TO_INNER(RefreshCompartmentPrincipal, (), /* void */ );
5819
5820 JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
5821 nsJSPrincipals::get(mDoc->NodePrincipal()));
5822 }
5823
5824 static already_AddRefed<nsIDocShellTreeItem>
5825 GetCallerDocShellTreeItem()
5826 {
5827 JSContext *cx = nsContentUtils::GetCurrentJSContext();
5828 nsCOMPtr<nsIDocShellTreeItem> callerItem;
5829
5830 if (cx) {
5831 nsCOMPtr<nsIWebNavigation> callerWebNav =
5832 do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx));
5833
5834 callerItem = do_QueryInterface(callerWebNav);
5835 }
5836
5837 return callerItem.forget();
5838 }
5839
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");
5846
5847 nsCOMPtr<nsIDocShellTreeItem> caller;
5848 if (aLookForCallerOnJSStack) {
5849 caller = GetCallerDocShellTreeItem();
5850 }
5851
5852 if (!caller) {
5853 caller = mDocShell;
5854 }
5855
5856 nsCOMPtr<nsIDocShellTreeItem> namedItem;
5857 mDocShell->FindItemWithName(PromiseFlatString(aName).get(), nullptr, caller,
5858 getter_AddRefs(namedItem));
5859 return namedItem != nullptr;
5860 }
5861
5862 already_AddRefed<nsIWidget>
5863 nsGlobalWindow::GetMainWidget()
5864 {
5865 FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
5866
5867 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
5868
5869 nsCOMPtr<nsIWidget> widget;
5870
5871 if (treeOwnerAsWin) {
5872 treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
5873 }
5874
5875 return widget.forget();
5876 }
5877
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 }
5889
5890 void
5891 nsGlobalWindow::SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError)
5892 {
5893 aError = SetFullScreenInternal(aFullScreen, true);
5894 }
5895
5896 NS_IMETHODIMP
5897 nsGlobalWindow::SetFullScreen(bool aFullScreen)
5898 {
5899 return SetFullScreenInternal(aFullScreen, true);
5900 }
5901
5902 nsresult
5903 nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
5904 {
5905 FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);
5906
5907 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
5908
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 }
5917
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);
5928
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;
5933
5934 // If we are already in full screen mode, just return.
5935 if (mFullScreen == aFullScreen)
5936 return NS_OK;
5937
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 }
5943
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 }
5951
5952 // Set this before so if widget sends an event indicating its
5953 // gone full screen, the state trap above works.
5954 mFullScreen = aFullScreen;
5955
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 }
5965
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 }
5973
5974 if (!mWakeLock && mFullScreen) {
5975 nsRefPtr<power::PowerManagerService> pmService =
5976 power::PowerManagerService::GetInstance();
5977 NS_ENSURE_TRUE(pmService, NS_OK);
5978
5979 ErrorResult rv;
5980 mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"),
5981 this, rv);
5982 if (rv.Failed()) {
5983 return rv.ErrorCode();
5984 }
5985
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 }
5992
5993 return NS_OK;
5994 }
5995
5996 bool
5997 nsGlobalWindow::GetFullScreen(ErrorResult& aError)
5998 {
5999 FORWARD_TO_OUTER_OR_THROW(GetFullScreen, (aError), aError, false);
6000
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 }
6015
6016 // We are the root window, or something went wrong. Return our internal value.
6017 return mFullScreen;
6018 }
6019
6020 NS_IMETHODIMP
6021 nsGlobalWindow::GetFullScreen(bool* aFullScreen)
6022 {
6023 ErrorResult rv;
6024 *aFullScreen = GetFullScreen(rv);
6025
6026 return rv.ErrorCode();
6027 }
6028
6029 NS_IMETHODIMP
6030 nsGlobalWindow::Dump(const nsAString& aStr)
6031 {
6032 if (!nsContentUtils::DOMWindowDumpEnabled()) {
6033 return NS_OK;
6034 }
6035
6036 char *cstr = ToNewUTF8String(aStr);
6037
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
6047
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 }
6060
6061 return NS_OK;
6062 }
6063
6064 void
6065 nsGlobalWindow::EnsureReflowFlushAndPaint()
6066 {
6067 MOZ_ASSERT(IsOuterWindow());
6068 NS_ASSERTION(mDocShell, "EnsureReflowFlushAndPaint() called with no "
6069 "docshell!");
6070
6071 if (!mDocShell)
6072 return;
6073
6074 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
6075
6076 if (!presShell)
6077 return;
6078
6079 // Flush pending reflows.
6080 if (mDoc) {
6081 mDoc->FlushPendingNotifications(Flush_Layout);
6082 }
6083
6084 // Unsuppress painting.
6085 presShell->UnsuppressPainting();
6086 }
6087
6088 NS_IMETHODIMP
6089 nsGlobalWindow::GetTextZoom(float *aZoom)
6090 {
6091 FORWARD_TO_OUTER(GetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED);
6092
6093 if (mDocShell) {
6094 nsCOMPtr<nsIContentViewer> contentViewer;
6095 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
6096 nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(contentViewer));
6097
6098 if (markupViewer) {
6099 return markupViewer->GetTextZoom(aZoom);
6100 }
6101 }
6102 return NS_ERROR_FAILURE;
6103 }
6104
6105 NS_IMETHODIMP
6106 nsGlobalWindow::SetTextZoom(float aZoom)
6107 {
6108 FORWARD_TO_OUTER(SetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED);
6109
6110 if (mDocShell) {
6111 nsCOMPtr<nsIContentViewer> contentViewer;
6112 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
6113 nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(contentViewer));
6114
6115 if (markupViewer)
6116 return markupViewer->SetTextZoom(aZoom);
6117 }
6118 return NS_ERROR_FAILURE;
6119 }
6120
6121 // static
6122 void
6123 nsGlobalWindow::MakeScriptDialogTitle(nsAString &aOutTitle)
6124 {
6125 aOutTitle.Truncate();
6126
6127 // Try to get a host from the running principal -- this will do the
6128 // right thing for javascript: and data: documents.
6129
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));
6137
6138 if (NS_SUCCEEDED(rv) && principal) {
6139 nsCOMPtr<nsIURI> uri;
6140 rv = principal->GetURI(getter_AddRefs(uri));
6141
6142 if (NS_SUCCEEDED(rv) && uri) {
6143 // remove user:pass for privacy and spoof prevention
6144
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);
6152
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
6157
6158 nsAutoCString prepath;
6159 fixedURI->GetPrePath(prepath);
6160
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 }
6178
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 }
6187
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 }
6194
6195 bool
6196 nsGlobalWindow::CanMoveResizeWindows()
6197 {
6198 MOZ_ASSERT(IsOuterWindow());
6199
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 }
6207
6208 if (!CanSetProperty("dom.disable_window_move_resize")) {
6209 return false;
6210 }
6211
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 }
6222
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 }
6232
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 }
6243
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());
6252
6253 if (!AreDialogsEnabled()) {
6254 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6255 return false;
6256 }
6257
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);
6262
6263 // Before bringing up the window, unsuppress painting and flush
6264 // pending reflows.
6265 EnsureReflowFlushAndPaint();
6266
6267 nsAutoString title;
6268 MakeScriptDialogTitle(title);
6269
6270 // Remove non-terminating null characters from the
6271 // string. See bug #310037.
6272 nsAutoString final;
6273 nsContentUtils::StripNullChars(aMessage, final);
6274
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 }
6282
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 }
6289
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 }
6294
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);
6302
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);
6308
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 }
6316
6317 return result;
6318 }
6319
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 }
6326
6327 NS_IMETHODIMP
6328 nsGlobalWindow::Alert(const nsAString& aString)
6329 {
6330 ErrorResult rv;
6331 Alert(aString, rv);
6332
6333 return rv.ErrorCode();
6334 }
6335
6336 bool
6337 nsGlobalWindow::Confirm(const nsAString& aMessage, ErrorResult& aError)
6338 {
6339 FORWARD_TO_OUTER_OR_THROW(Confirm, (aMessage, aError), aError, false);
6340
6341 return AlertOrConfirm(/* aAlert = */ false, aMessage, aError);
6342 }
6343
6344 NS_IMETHODIMP
6345 nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
6346 {
6347 ErrorResult rv;
6348 *aReturn = Confirm(aString, rv);
6349
6350 return rv.ErrorCode();
6351 }
6352
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, );
6361
6362 SetDOMStringToNull(aReturn);
6363
6364 if (!AreDialogsEnabled()) {
6365 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6366 return;
6367 }
6368
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);
6373
6374 // Before bringing up the window, unsuppress painting and flush
6375 // pending reflows.
6376 EnsureReflowFlushAndPaint();
6377
6378 nsAutoString title;
6379 MakeScriptDialogTitle(title);
6380
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);
6386
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 }
6394
6395 nsCOMPtr<nsIPrompt> prompt;
6396 aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt),
6397 getter_AddRefs(prompt));
6398 if (aError.Failed()) {
6399 return;
6400 }
6401
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 }
6406
6407 // Pass in the default value, if any.
6408 char16_t *inoutValue = ToNewUnicode(fixedInitial);
6409 bool disallowDialog = false;
6410
6411 nsXPIDLString label;
6412 if (ShouldPromptToBlockDialogs()) {
6413 nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
6414 "ScriptDialogLabel", label);
6415 }
6416
6417 nsAutoSyncOperation sync(mDoc);
6418 bool ok;
6419 aError = prompt->Prompt(title.get(), fixedMessage.get(),
6420 &inoutValue, label.get(), &disallowDialog, &ok);
6421
6422 if (disallowDialog) {
6423 DisableDialogs();
6424 }
6425
6426 if (aError.Failed()) {
6427 return;
6428 }
6429
6430 nsAdoptingString outValue(inoutValue);
6431
6432 if (ok && outValue) {
6433 aReturn.Assign(outValue);
6434 }
6435 }
6436
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);
6443
6444 return rv.ErrorCode();
6445 }
6446
6447 void
6448 nsGlobalWindow::Focus(ErrorResult& aError)
6449 {
6450 FORWARD_TO_OUTER_OR_THROW(Focus, (aError), aError, );
6451
6452 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6453 if (!fm) {
6454 return;
6455 }
6456
6457 nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
6458
6459 bool isVisible = false;
6460 if (baseWin) {
6461 baseWin->GetVisibility(&isVisible);
6462 }
6463
6464 if (!isVisible) {
6465 // A hidden tab is being focused, ignore this call.
6466 return;
6467 }
6468
6469 nsIDOMWindow *caller = nsContentUtils::GetWindowFromCaller();
6470 nsCOMPtr<nsIDOMWindow> opener;
6471 GetOpener(getter_AddRefs(opener));
6472
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);
6479
6480 nsCOMPtr<nsIDOMWindow> activeWindow;
6481 fm->GetActiveWindow(getter_AddRefs(activeWindow));
6482
6483 nsCOMPtr<nsIDocShellTreeItem> rootItem;
6484 mDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
6485 nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem);
6486 bool isActive = (rootWin == activeWindow);
6487
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 }
6495
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 }
6501
6502 if (!mDocShell) {
6503 return;
6504 }
6505
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 }
6519
6520 if (lookForPresShell) {
6521 mDocShell->GetEldestPresShell(getter_AddRefs(presShell));
6522 }
6523
6524 nsCOMPtr<nsIDocShellTreeItem> parentDsti;
6525 mDocShell->GetParent(getter_AddRefs(parentDsti));
6526
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 }
6534
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 }
6555
6556 NS_IMETHODIMP
6557 nsGlobalWindow::Focus()
6558 {
6559 ErrorResult rv;
6560 Focus(rv);
6561
6562 return rv.ErrorCode();
6563 }
6564
6565 void
6566 nsGlobalWindow::Blur(ErrorResult& aError)
6567 {
6568 FORWARD_TO_OUTER_OR_THROW(Blur, (aError), aError, );
6569
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 }
6575
6576 // If embedding apps don't implement nsIEmbeddingSiteWindow, we
6577 // shouldn't throw exceptions to web content.
6578
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();
6584
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 }
6596
6597 NS_IMETHODIMP
6598 nsGlobalWindow::Blur()
6599 {
6600 ErrorResult rv;
6601 Blur(rv);
6602
6603 return rv.ErrorCode();
6604 }
6605
6606 void
6607 nsGlobalWindow::Back(ErrorResult& aError)
6608 {
6609 FORWARD_TO_OUTER_OR_THROW(Back, (aError), aError, );
6610
6611 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6612 if (!webNav) {
6613 aError.Throw(NS_ERROR_FAILURE);
6614 return;
6615 }
6616
6617 aError = webNav->GoBack();
6618 }
6619
6620 NS_IMETHODIMP
6621 nsGlobalWindow::Back()
6622 {
6623 ErrorResult rv;
6624 Back(rv);
6625
6626 return rv.ErrorCode();
6627 }
6628
6629 void
6630 nsGlobalWindow::Forward(ErrorResult& aError)
6631 {
6632 FORWARD_TO_OUTER_OR_THROW(Forward, (aError), aError, );
6633
6634 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6635 if (!webNav) {
6636 aError.Throw(NS_ERROR_FAILURE);
6637 return;
6638 }
6639
6640 aError = webNav->GoForward();
6641 }
6642
6643 NS_IMETHODIMP
6644 nsGlobalWindow::Forward()
6645 {
6646 ErrorResult rv;
6647 Forward(rv);
6648
6649 return rv.ErrorCode();
6650 }
6651
6652 void
6653 nsGlobalWindow::Home(ErrorResult& aError)
6654 {
6655 FORWARD_TO_OUTER_OR_THROW(Home, (aError), aError, );
6656
6657 if (!mDocShell) {
6658 return;
6659 }
6660
6661 nsAdoptingString homeURL =
6662 Preferences::GetLocalizedString(PREF_BROWSER_STARTUP_HOMEPAGE);
6663
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 }
6671
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('|');
6684
6685 if (firstPipe > 0) {
6686 homeURL.Truncate(firstPipe);
6687 }
6688 }
6689 #endif
6690
6691 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6692 if (!webNav) {
6693 aError.Throw(NS_ERROR_FAILURE);
6694 return;
6695 }
6696
6697 aError = webNav->LoadURI(homeURL.get(),
6698 nsIWebNavigation::LOAD_FLAGS_NONE,
6699 nullptr,
6700 nullptr,
6701 nullptr);
6702 }
6703
6704 NS_IMETHODIMP
6705 nsGlobalWindow::Home()
6706 {
6707 ErrorResult rv;
6708 Home(rv);
6709
6710 return rv.ErrorCode();
6711 }
6712
6713 void
6714 nsGlobalWindow::Stop(ErrorResult& aError)
6715 {
6716 FORWARD_TO_OUTER_OR_THROW(Stop, (aError), aError, );
6717
6718 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
6719 if (webNav) {
6720 aError = webNav->Stop(nsIWebNavigation::STOP_ALL);
6721 }
6722 }
6723
6724 NS_IMETHODIMP
6725 nsGlobalWindow::Stop()
6726 {
6727 ErrorResult rv;
6728 Stop(rv);
6729
6730 return rv.ErrorCode();
6731 }
6732
6733 void
6734 nsGlobalWindow::Print(ErrorResult& aError)
6735 {
6736 #ifdef NS_PRINTING
6737 FORWARD_TO_OUTER_OR_THROW(Print, (aError), aError, );
6738
6739 if (Preferences::GetBool("dom.disable_window_print", false)) {
6740 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6741 return;
6742 }
6743
6744 if (!AreDialogsEnabled()) {
6745 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6746 return;
6747 }
6748
6749 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
6750 aError.Throw(NS_ERROR_NOT_AVAILABLE);
6751 return;
6752 }
6753
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);
6760
6761 nsCOMPtr<nsIPrintSettingsService> printSettingsService =
6762 do_GetService("@mozilla.org/gfx/printsettings-service;1");
6763
6764 nsCOMPtr<nsIPrintSettings> printSettings;
6765 if (printSettingsService) {
6766 bool printSettingsAreGlobal =
6767 Preferences::GetBool("print.use_global_printsettings", false);
6768
6769 if (printSettingsAreGlobal) {
6770 printSettingsService->GetGlobalPrintSettings(getter_AddRefs(printSettings));
6771
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 }
6785
6786 EnterModalState();
6787 webBrowserPrint->Print(printSettings, nullptr);
6788 LeaveModalState();
6789
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 }
6809
6810 NS_IMETHODIMP
6811 nsGlobalWindow::Print()
6812 {
6813 ErrorResult rv;
6814 Print(rv);
6815
6816 return rv.ErrorCode();
6817 }
6818
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, );
6823
6824 /*
6825 * If caller is not chrome and the user has not explicitly exempted the site,
6826 * prevent window.moveTo() by exiting early
6827 */
6828
6829 if (!CanMoveResizeWindows() || IsFrame()) {
6830 return;
6831 }
6832
6833 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6834 if (!treeOwnerAsWin) {
6835 aError.Throw(NS_ERROR_FAILURE);
6836 return;
6837 }
6838
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);
6842
6843 nsIntSize devPos = CSSToDevIntPixels(cssPos);
6844
6845 aError = treeOwnerAsWin->SetPosition(devPos.width, devPos.height);
6846 }
6847
6848 NS_IMETHODIMP
6849 nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos)
6850 {
6851 ErrorResult rv;
6852 MoveTo(aXPos, aYPos, rv);
6853
6854 return rv.ErrorCode();
6855 }
6856
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, );
6861
6862 /*
6863 * If caller is not chrome and the user has not explicitly exempted the site,
6864 * prevent window.moveBy() by exiting early
6865 */
6866
6867 if (!CanMoveResizeWindows() || IsFrame()) {
6868 return;
6869 }
6870
6871 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6872 if (!treeOwnerAsWin) {
6873 aError.Throw(NS_ERROR_FAILURE);
6874 return;
6875 }
6876
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.
6880
6881 int32_t x, y;
6882 aError = treeOwnerAsWin->GetPosition(&x, &y);
6883 if (aError.Failed()) {
6884 return;
6885 }
6886
6887 // mild abuse of a "size" object so we don't need more helper functions
6888 nsIntSize cssPos(DevToCSSIntPixels(nsIntSize(x, y)));
6889
6890 cssPos.width += aXDif;
6891 cssPos.height += aYDif;
6892
6893 CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
6894
6895 nsIntSize newDevPos(CSSToDevIntPixels(cssPos));
6896
6897 aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height);
6898 }
6899
6900 NS_IMETHODIMP
6901 nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif)
6902 {
6903 ErrorResult rv;
6904 MoveBy(aXDif, aYDif, rv);
6905
6906 return rv.ErrorCode();
6907 }
6908
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, );
6913
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 }
6926
6927 /*
6928 * If caller is not chrome and the user has not explicitly exempted the site,
6929 * prevent window.resizeTo() by exiting early
6930 */
6931
6932 if (!CanMoveResizeWindows() || IsFrame()) {
6933 return;
6934 }
6935
6936 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6937 if (!treeOwnerAsWin) {
6938 aError.Throw(NS_ERROR_FAILURE);
6939 return;
6940 }
6941
6942 nsIntSize cssSize(aWidth, aHeight);
6943 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
6944
6945 nsIntSize devSz(CSSToDevIntPixels(cssSize));
6946
6947 aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true);
6948 }
6949
6950 NS_IMETHODIMP
6951 nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight)
6952 {
6953 ErrorResult rv;
6954 ResizeTo(aWidth, aHeight, rv);
6955
6956 return rv.ErrorCode();
6957 }
6958
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, );
6964
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 }
6974
6975 size.width += aWidthDif;
6976 size.height += aHeightDif;
6977
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 }
6984
6985 /*
6986 * If caller is not chrome and the user has not explicitly exempted the site,
6987 * prevent window.resizeBy() by exiting early
6988 */
6989
6990 if (!CanMoveResizeWindows() || IsFrame()) {
6991 return;
6992 }
6993
6994 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
6995 if (!treeOwnerAsWin) {
6996 aError.Throw(NS_ERROR_FAILURE);
6997 return;
6998 }
6999
7000 int32_t width, height;
7001 aError = treeOwnerAsWin->GetSize(&width, &height);
7002 if (aError.Failed()) {
7003 return;
7004 }
7005
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.
7009
7010 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
7011
7012 cssSize.width += aWidthDif;
7013 cssSize.height += aHeightDif;
7014
7015 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7016
7017 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
7018
7019 aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true);
7020 }
7021
7022 NS_IMETHODIMP
7023 nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif)
7024 {
7025 ErrorResult rv;
7026 ResizeBy(aWidthDif, aHeightDif, rv);
7027
7028 return rv.ErrorCode();
7029 }
7030
7031 void
7032 nsGlobalWindow::SizeToContent(ErrorResult& aError)
7033 {
7034 FORWARD_TO_OUTER_OR_THROW(SizeToContent, (aError), aError, );
7035
7036 if (!mDocShell) {
7037 return;
7038 }
7039
7040 /*
7041 * If caller is not chrome and the user has not explicitly exempted the site,
7042 * prevent window.sizeToContent() by exiting early
7043 */
7044
7045 if (!CanMoveResizeWindows() || IsFrame()) {
7046 return;
7047 }
7048
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 }
7058
7059 int32_t width, height;
7060 aError = markupViewer->GetContentSize(&width, &height);
7061 if (aError.Failed()) {
7062 return;
7063 }
7064
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 }
7072
7073 nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
7074 CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
7075
7076 nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
7077
7078 aError = treeOwner->SizeShellTo(mDocShell, newDevSize.width,
7079 newDevSize.height);
7080 }
7081
7082 NS_IMETHODIMP
7083 nsGlobalWindow::SizeToContent()
7084 {
7085 ErrorResult rv;
7086 SizeToContent(rv);
7087
7088 return rv.ErrorCode();
7089 }
7090
7091 NS_IMETHODIMP
7092 nsGlobalWindow::GetWindowRoot(nsIDOMEventTarget **aWindowRoot)
7093 {
7094 nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
7095 return CallQueryInterface(root, aWindowRoot);
7096 }
7097
7098 already_AddRefed<nsPIWindowRoot>
7099 nsGlobalWindow::GetTopWindowRoot()
7100 {
7101 nsPIDOMWindow* piWin = GetPrivateRoot();
7102 if (!piWin) {
7103 return nullptr;
7104 }
7105
7106 nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler());
7107 return window.forget();
7108 }
7109
7110 NS_IMETHODIMP
7111 nsGlobalWindow::Scroll(int32_t aXScroll, int32_t aYScroll)
7112 {
7113 ScrollTo(CSSIntPoint(aXScroll, aYScroll));
7114 return NS_OK;
7115 }
7116
7117 NS_IMETHODIMP
7118 nsGlobalWindow::ScrollTo(int32_t aXScroll, int32_t aYScroll)
7119 {
7120 ScrollTo(CSSIntPoint(aXScroll, aYScroll));
7121 return NS_OK;
7122 }
7123
7124 void
7125 nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll)
7126 {
7127 FlushPendingNotifications(Flush_Layout);
7128 nsIScrollableFrame *sf = GetScrollFrame();
7129
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;
7137
7138 CSSIntPoint scroll(aScroll);
7139 if (scroll.x > maxpx) {
7140 scroll.x = maxpx;
7141 }
7142
7143 if (scroll.y > maxpx) {
7144 scroll.y = maxpx;
7145 }
7146 sf->ScrollToCSSPixels(scroll);
7147 }
7148 }
7149
7150 NS_IMETHODIMP
7151 nsGlobalWindow::ScrollBy(int32_t aXScrollDif, int32_t aYScrollDif)
7152 {
7153 FlushPendingNotifications(Flush_Layout);
7154 nsIScrollableFrame *sf = GetScrollFrame();
7155
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 }
7164
7165 return NS_OK;
7166 }
7167
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 }
7180
7181 return NS_OK;
7182 }
7183
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 }
7196
7197 return NS_OK;
7198 }
7199
7200 void
7201 nsGlobalWindow::ClearTimeout(int32_t aHandle, ErrorResult& aError)
7202 {
7203 if (aHandle > 0) {
7204 ClearTimeoutOrInterval(aHandle, aError);
7205 }
7206 }
7207
7208 NS_IMETHODIMP
7209 nsGlobalWindow::ClearTimeout(int32_t aHandle)
7210 {
7211 ErrorResult rv;
7212 ClearTimeout(aHandle, rv);
7213
7214 return rv.ErrorCode();
7215 }
7216
7217 void
7218 nsGlobalWindow::ClearInterval(int32_t aHandle, ErrorResult& aError)
7219 {
7220 if (aHandle > 0) {
7221 ClearTimeoutOrInterval(aHandle, aError);
7222 }
7223 }
7224
7225 NS_IMETHODIMP
7226 nsGlobalWindow::ClearInterval(int32_t aHandle)
7227 {
7228 ErrorResult rv;
7229 ClearInterval(aHandle, rv);
7230
7231 return rv.ErrorCode();
7232 }
7233
7234 NS_IMETHODIMP
7235 nsGlobalWindow::SetTimeout(int32_t *_retval)
7236 {
7237 return SetTimeoutOrInterval(false, _retval);
7238 }
7239
7240 NS_IMETHODIMP
7241 nsGlobalWindow::SetInterval(int32_t *_retval)
7242 {
7243 return SetTimeoutOrInterval(true, _retval);
7244 }
7245
7246 NS_IMETHODIMP
7247 nsGlobalWindow::SetResizable(bool aResizable)
7248 {
7249 // nop
7250
7251 return NS_OK;
7252 }
7253
7254 NS_IMETHODIMP
7255 nsGlobalWindow::CaptureEvents()
7256 {
7257 if (mDoc) {
7258 mDoc->WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
7259 }
7260
7261 return NS_OK;
7262 }
7263
7264 NS_IMETHODIMP
7265 nsGlobalWindow::ReleaseEvents()
7266 {
7267 if (mDoc) {
7268 mDoc->WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
7269 }
7270
7271 return NS_OK;
7272 }
7273
7274 static
7275 bool IsPopupBlocked(nsIDocument* aDoc)
7276 {
7277 nsCOMPtr<nsIPopupWindowManager> pm =
7278 do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
7279
7280 if (!pm) {
7281 return false;
7282 }
7283
7284 if (!aDoc) {
7285 return true;
7286 }
7287
7288 uint32_t permission = nsIPopupWindowManager::ALLOW_POPUP;
7289 pm->TestPermission(aDoc->NodePrincipal(), &permission);
7290 return permission == nsIPopupWindowManager::DENY_POPUP;
7291 }
7292
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);
7313
7314 bool defaultActionEnabled;
7315 aDoc->DispatchEvent(event, &defaultActionEnabled);
7316 }
7317 }
7318 }
7319
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 }
7327
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 }
7336
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 }
7341
7342 bool
7343 nsGlobalWindow::PopupWhitelisted()
7344 {
7345 if (!IsPopupBlocked(mDoc))
7346 return true;
7347
7348 nsCOMPtr<nsIDOMWindow> parent;
7349
7350 if (NS_FAILED(GetParent(getter_AddRefs(parent))) ||
7351 parent == static_cast<nsIDOMWindow*>(this))
7352 {
7353 return false;
7354 }
7355
7356 return static_cast<nsGlobalWindow*>
7357 (static_cast<nsIDOMWindow*>
7358 (parent.get()))->PopupWhitelisted();
7359 }
7360
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());
7371
7372 NS_ASSERTION(mDocShell, "Must have docshell");
7373
7374 if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
7375 return openAllowed;
7376 }
7377
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 }
7389
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 }
7396
7397 return abuse;
7398 }
7399
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
7411
7412 nsCOMPtr<nsIDOMWindow> topWindow;
7413 GetTop(getter_AddRefs(topWindow));
7414 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(topWindow);
7415 if (!window) {
7416 return;
7417 }
7418
7419 nsCOMPtr<nsIDocument> topDoc = window->GetDoc();
7420 nsCOMPtr<nsIURI> popupURI;
7421
7422 // build the URI of the would-have-been popup window
7423 // (see nsWindowWatcher::URIfromURL)
7424
7425 // first, fetch the opener's base URI
7426
7427 nsIURI *baseURL = nullptr;
7428
7429 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7430 nsCOMPtr<nsPIDOMWindow> contextWindow;
7431
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 }
7441
7442 nsCOMPtr<nsIDocument> doc = contextWindow->GetDoc();
7443 if (doc)
7444 baseURL = doc->GetDocBaseURI();
7445
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));
7451
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 }
7460
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 }
7469
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 }
7485
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 }
7501
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 }
7520
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);
7538
7539 }
7540
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 }
7554
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 }
7568
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 }
7576
7577 nsAXPCNativeCallContext *ncc = nullptr;
7578 nsresult rv = nsContentUtils::XPConnect()->
7579 GetCurrentNativeCallContext(&ncc);
7580 NS_ENSURE_SUCCESS(rv, rv);
7581
7582 if (!ncc)
7583 return NS_ERROR_NOT_AVAILABLE;
7584
7585 JSContext *cx = nullptr;
7586
7587 rv = ncc->GetJSContext(&cx);
7588 NS_ENSURE_SUCCESS(rv, rv);
7589
7590 uint32_t argc;
7591 JS::Value *argv = nullptr;
7592
7593 // XXX - need to get this as nsISupports?
7594 ncc->GetArgc(&argc);
7595 ncc->GetArgvPtr(&argv);
7596
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);
7603
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 }
7615
7616 already_AddRefed<nsIDOMWindow>
7617 nsGlobalWindow::GetFrames(ErrorResult& aError)
7618 {
7619 FORWARD_TO_OUTER_OR_THROW(GetFrames, (aError), aError, nullptr);
7620
7621 nsRefPtr<nsGlobalWindow> frames(this);
7622 FlushPendingNotifications(Flush_ContentAndNotify);
7623 return frames.forget();
7624 }
7625
7626 NS_IMETHODIMP
7627 nsGlobalWindow::GetFrames(nsIDOMWindow** aFrames)
7628 {
7629 ErrorResult rv;
7630 nsCOMPtr<nsIDOMWindow> frames = GetFrames(rv);
7631 frames.forget(aFrames);
7632
7633 return rv.ErrorCode();
7634 }
7635
7636 JSObject* nsGlobalWindow::CallerGlobal()
7637 {
7638 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7639 if (!cx) {
7640 NS_ERROR("Please don't call this method from C++!");
7641
7642 return nullptr;
7643 }
7644
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 }
7668
7669
7670 nsGlobalWindow*
7671 nsGlobalWindow::CallerInnerWindow()
7672 {
7673 JSContext *cx = nsContentUtils::GetCurrentJSContext();
7674 NS_ENSURE_TRUE(cx, nullptr);
7675 JS::Rooted<JSObject*> scope(cx, CallerGlobal());
7676
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);
7695
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;
7704
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 }
7713
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;
7721
7722 nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDoc);
7723 return thirdPartyUtil->GetFirstPartyIsolationURI(NULL, doc, aFirstPartyIsolationURI);
7724 }
7725
7726
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
7735
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 }
7749
7750 ~PostMessageEvent()
7751 {
7752 MOZ_COUNT_DTOR(PostMessageEvent);
7753 }
7754
7755 JSAutoStructuredCloneBuffer& Buffer()
7756 {
7757 return mBuffer;
7758 }
7759
7760 bool StoreISupports(nsISupports* aSupports)
7761 {
7762 mSupportsArray.AppendElement(aSupports);
7763 return true;
7764 }
7765
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 };
7775
7776 namespace {
7777
7778 struct StructuredCloneInfo {
7779 PostMessageEvent* event;
7780 bool subsumes;
7781 nsPIDOMWindow* window;
7782 nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports;
7783 };
7784
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");
7794
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 }
7803
7804 const JSStructuredCloneCallbacks* runtimeCallbacks =
7805 js::GetContextStructuredCloneCallbacks(cx);
7806
7807 if (runtimeCallbacks) {
7808 return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
7809 }
7810
7811 return nullptr;
7812 }
7813
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!");
7822
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();
7829
7830 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
7831 if (blob && scInfo->subsumes)
7832 scTag = SCTAG_DOM_BLOB;
7833
7834 nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
7835 if (list && scInfo->subsumes)
7836 scTag = SCTAG_DOM_FILELIST;
7837
7838 if (scTag)
7839 return JS_WriteUint32Pair(writer, scTag, 0) &&
7840 JS_WriteBytes(writer, &supports, sizeof(supports)) &&
7841 scInfo->event->StoreISupports(supports);
7842 }
7843
7844 const JSStructuredCloneCallbacks* runtimeCallbacks =
7845 js::GetContextStructuredCloneCallbacks(cx);
7846
7847 if (runtimeCallbacks) {
7848 return runtimeCallbacks->write(cx, writer, obj, nullptr);
7849 }
7850
7851 return false;
7852 }
7853
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!");
7864
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);
7869
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 }
7875
7876 return true;
7877 }
7878
7879 return false;
7880 }
7881
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!");
7893
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 }
7903
7904 newPort = port->Clone();
7905 scInfo->ports.Put(port, newPort);
7906
7907 *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
7908 *aOwnership = JS::SCTAG_TMO_CUSTOM;
7909 *aContent = newPort;
7910 *aExtraData = 0;
7911
7912 return true;
7913 }
7914 }
7915
7916 return false;
7917 }
7918
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!");
7925
7926 if (MessageChannel::PrefEnabled() && aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
7927 nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent));
7928 scInfo->ports.Remove(port);
7929 }
7930 }
7931
7932 JSStructuredCloneCallbacks kPostMessageCallbacks = {
7933 PostMessageReadStructuredClone,
7934 PostMessageWriteStructuredClone,
7935 nullptr,
7936 PostMessageReadTransferStructuredClone,
7937 PostMessageTransferStructuredClone,
7938 PostMessageFreeTransferStructuredClone
7939 };
7940
7941 } // anonymous namespace
7942
7943 static PLDHashOperator
7944 PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
7945 {
7946 nsTArray<nsRefPtr<MessagePortBase> > *array =
7947 static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
7948
7949 array->AppendElement(aKey);
7950 return PL_DHASH_NEXT;
7951 }
7952
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!");
7960
7961 AutoJSAPI jsapi;
7962 JSContext* cx = jsapi.cx();
7963
7964 // If we bailed before this point we're going to leak mMessage, but
7965 // that's probably better than crashing.
7966
7967 nsRefPtr<nsGlobalWindow> targetWindow;
7968 if (mTargetWindow->IsClosedOrClosing() ||
7969 !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
7970 targetWindow->IsClosedOrClosing())
7971 return NS_OK;
7972
7973 NS_ABORT_IF_FALSE(targetWindow->IsInnerWindow(),
7974 "we ordered an inner window!");
7975 JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor());
7976
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;
7992
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 }
8002
8003 // Deserialize the structured clone data
8004 JS::Rooted<JS::Value> messageData(cx);
8005 StructuredCloneInfo scInfo;
8006 scInfo.event = this;
8007 scInfo.window = targetWindow;
8008
8009 if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
8010 return NS_ERROR_DOM_DATA_CLONE_ERR;
8011 }
8012
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);
8018
8019 event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */,
8020 false /*cancelable */, messageData, mCallerOrigin,
8021 EmptyString(), mSource);
8022
8023 nsTArray<nsRefPtr<MessagePortBase> > ports;
8024 scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports);
8025 event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
8026
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.
8031
8032 nsIPresShell *shell = targetWindow->mDoc->GetShell();
8033 nsRefPtr<nsPresContext> presContext;
8034 if (shell)
8035 presContext = shell->GetPresContext();
8036
8037 event->SetTrusted(mTrustedCaller);
8038 WidgetEvent* internalEvent = event->GetInternalNSEvent();
8039
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 }
8048
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, );
8058
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 //
8066
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");
8073
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 }
8093
8094 nsCOMPtr<nsIURI> callerOuterURI;
8095 if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) {
8096 return;
8097 }
8098
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 }
8120
8121 // Convert the provided origin string into a URI for comparison purposes.
8122 nsCOMPtr<nsIPrincipal> providedPrincipal;
8123
8124 if (aTargetOrigin.EqualsASCII("/")) {
8125 providedPrincipal = BrokenGetEntryGlobal()->PrincipalOrNull();
8126 if (NS_WARN_IF(!providedPrincipal))
8127 return;
8128 }
8129
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 }
8137
8138 if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
8139 NS_FAILED(originURI->SetPath(EmptyCString()))) {
8140 return;
8141 }
8142
8143 nsCOMPtr<nsIScriptSecurityManager> ssm =
8144 nsContentUtils::GetSecurityManager();
8145 MOZ_ASSERT(ssm);
8146
8147 nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSubjectPrincipal();
8148 MOZ_ASSERT(principal);
8149
8150 uint32_t appId;
8151 if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId))))
8152 return;
8153
8154 bool isInBrowser;
8155 if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser))))
8156 return;
8157
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 }
8166
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());
8177
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;
8183
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 }
8193
8194 aError = NS_DispatchToCurrentThread(event);
8195 }
8196
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();
8206
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());
8211
8212 transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements));
8213 if (transferArray.isNull()) {
8214 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
8215 return;
8216 }
8217 }
8218
8219 PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aError);
8220 }
8221
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);
8230
8231 return rv.ErrorCode();
8232 }
8233
8234 class nsCloseEvent : public nsRunnable {
8235
8236 nsRefPtr<nsGlobalWindow> mWindow;
8237 bool mIndirect;
8238
8239 nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect)
8240 : mWindow(aWindow)
8241 , mIndirect(aIndirect)
8242 {}
8243
8244 public:
8245
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 }
8254
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 }
8264
8265 };
8266
8267 bool
8268 nsGlobalWindow::CanClose()
8269 {
8270 if (!mDocShell)
8271 return true;
8272
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.
8277
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;
8285
8286 rv = cv->RequestWindowClose(&canClose);
8287 if (NS_SUCCEEDED(rv) && !canClose)
8288 return false;
8289 }
8290
8291 return true;
8292 }
8293
8294 void
8295 nsGlobalWindow::Close(ErrorResult& aError)
8296 {
8297 FORWARD_TO_OUTER_OR_THROW(Close, (aError), aError, );
8298
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 }
8306
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 }
8312
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 }
8320
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");
8338
8339 return;
8340 }
8341 }
8342
8343 if (!mInClose && !mIsClosed && !CanClose()) {
8344 return;
8345 }
8346
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...
8353
8354 bool wasInClose = mInClose;
8355 mInClose = true;
8356
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...
8360
8361 mInClose = wasInClose;
8362 return;
8363 }
8364
8365 aError = FinalClose();
8366 }
8367
8368 NS_IMETHODIMP
8369 nsGlobalWindow::Close()
8370 {
8371 ErrorResult rv;
8372 Close(rv);
8373
8374 return rv.ErrorCode();
8375 }
8376
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.
8383
8384 return NS_OK;
8385 }
8386
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 }
8392
8393 mInClose = true;
8394
8395 DispatchCustomEvent("DOMWindowClose");
8396
8397 return FinalClose();
8398 }
8399
8400 nsresult
8401 nsGlobalWindow::FinalClose()
8402 {
8403 // Flag that we were closed.
8404 mIsClosed = true;
8405
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 }
8426
8427 return NS_OK;
8428 }
8429
8430
8431 void
8432 nsGlobalWindow::ReallyCloseWindow()
8433 {
8434 FORWARD_TO_OUTER_VOID(ReallyCloseWindow, ());
8435
8436 // Make sure we never reenter this method.
8437 mHavePendingClose = true;
8438
8439 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
8440
8441 // If there's no treeOwnerAsWin, this window must already be closed.
8442
8443 if (treeOwnerAsWin) {
8444
8445 // but if we're a browser window we could be in some nasty
8446 // self-destroying cascade that we should mostly ignore
8447
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));
8456
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 }
8476
8477 CleanUp();
8478 }
8479 }
8480
8481 void
8482 nsGlobalWindow::EnterModalState()
8483 {
8484 MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
8485
8486 // GetScriptableTop, not GetTop, so that EnterModalState works properly with
8487 // <iframe mozbrowser>.
8488 nsGlobalWindow* topWin = GetScriptableTop();
8489
8490 if (!topWin) {
8491 NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
8492 return;
8493 }
8494
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);
8506
8507 activeShell->SetCapturingContent(nullptr, 0);
8508
8509 if (activeShell) {
8510 nsRefPtr<nsFrameSelection> frameSelection = activeShell->FrameSelection();
8511 frameSelection->SetMouseDownState(false);
8512 }
8513 }
8514 }
8515
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 }
8522
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 }
8532
8533 if (topWin->mModalStateDepth == 0) {
8534 NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
8535
8536 mSuspendedDoc = topDoc;
8537 if (mSuspendedDoc) {
8538 mSuspendedDoc->SuppressEventHandling(nsIDocument::eAnimationsOnly);
8539 }
8540 }
8541 topWin->mModalStateDepth++;
8542 }
8543
8544 // static
8545 void
8546 nsGlobalWindow::RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow,
8547 nsGlobalWindow *aWindow)
8548 {
8549 nsGlobalWindow *inner;
8550
8551 // Return early if we're frozen or have no inner window.
8552 if (!(inner = aWindow->GetCurrentInnerWindowInternal()) ||
8553 inner->IsFrozen()) {
8554 return;
8555 }
8556
8557 inner->RunTimeout(nullptr);
8558
8559 // Check again if we're frozen since running pending timeouts
8560 // could've frozen us.
8561 if (inner->IsFrozen()) {
8562 return;
8563 }
8564
8565 nsCOMPtr<nsIDOMWindowCollection> frames;
8566 aWindow->GetFrames(getter_AddRefs(frames));
8567
8568 if (!frames) {
8569 return;
8570 }
8571
8572 uint32_t i, length;
8573 if (NS_FAILED(frames->GetLength(&length)) || !length) {
8574 return;
8575 }
8576
8577 for (i = 0; i < length && aTopWindow->mModalStateDepth == 0; i++) {
8578 nsCOMPtr<nsIDOMWindow> child;
8579 frames->Item(i, getter_AddRefs(child));
8580
8581 if (!child) {
8582 return;
8583 }
8584
8585 nsGlobalWindow *childWin =
8586 static_cast<nsGlobalWindow *>
8587 (static_cast<nsIDOMWindow *>
8588 (child.get()));
8589
8590 RunPendingTimeoutsRecursive(aTopWindow, childWin);
8591 }
8592 }
8593
8594 class nsPendingTimeoutRunner : public nsRunnable
8595 {
8596 public:
8597 nsPendingTimeoutRunner(nsGlobalWindow *aWindow)
8598 : mWindow(aWindow)
8599 {
8600 NS_ASSERTION(mWindow, "mWindow is null.");
8601 }
8602
8603 NS_IMETHOD Run()
8604 {
8605 nsGlobalWindow::RunPendingTimeoutsRecursive(mWindow, mWindow);
8606
8607 return NS_OK;
8608 }
8609
8610 private:
8611 nsRefPtr<nsGlobalWindow> mWindow;
8612 };
8613
8614 void
8615 nsGlobalWindow::LeaveModalState()
8616 {
8617 MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows");
8618
8619 nsGlobalWindow* topWin = GetScriptableTop();
8620
8621 if (!topWin) {
8622 NS_ERROR("Uh, LeaveModalState() called w/o a reachable top window?");
8623 return;
8624 }
8625
8626 topWin->mModalStateDepth--;
8627
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");
8632
8633 if (mSuspendedDoc) {
8634 nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc();
8635 mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eAnimationsOnly,
8636 currentDoc == mSuspendedDoc);
8637 mSuspendedDoc = nullptr;
8638 }
8639 }
8640
8641 // Remember the time of the last dialog quit.
8642 nsGlobalWindow *inner = topWin->GetCurrentInnerWindowInternal();
8643 if (inner)
8644 inner->mLastDialogQuitTime = TimeStamp::Now();
8645 }
8646
8647 bool
8648 nsGlobalWindow::IsInModalState()
8649 {
8650 nsGlobalWindow *topWin = GetScriptableTop();
8651
8652 if (!topWin) {
8653 NS_ERROR("Uh, IsInModalState() called w/o a reachable top window?");
8654
8655 return false;
8656 }
8657
8658 return topWin->mModalStateDepth != 0;
8659 }
8660
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 }
8672
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 }
8682
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 }
8695
8696 bool skipNukeCrossCompartment = false;
8697 #ifndef DEBUG
8698 nsCOMPtr<nsIAppStartup> appStartup =
8699 do_GetService(NS_APPSTARTUP_CONTRACTID);
8700
8701 if (appStartup) {
8702 appStartup->GetShuttingDown(&skipNukeCrossCompartment);
8703 }
8704 #endif
8705
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);
8712
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 }
8724
8725 return NS_OK;
8726 }
8727
8728 private:
8729 uint64_t mID;
8730 nsCString mTopic;
8731 nsWeakPtr mWindow;
8732 };
8733
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 }
8743
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 }
8757
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 }
8771
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 }
8782
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 }
8791
8792 mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
8793 }
8794
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 }
8812
8813 frameElement.forget(aFrameElement);
8814
8815 return NS_OK;
8816 }
8817
8818 Element*
8819 nsGlobalWindow::GetFrameElement(ErrorResult& aError)
8820 {
8821 FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aError), aError, nullptr);
8822
8823 if (!mDocShell || mDocShell->GetIsBrowserOrApp()) {
8824 return nullptr;
8825 }
8826
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 }
8838
8839 Element*
8840 nsGlobalWindow::GetRealFrameElement(ErrorResult& aError)
8841 {
8842 FORWARD_TO_OUTER_OR_THROW(GetRealFrameElement, (aError), aError, nullptr);
8843
8844 if (!mDocShell) {
8845 return nullptr;
8846 }
8847
8848 nsCOMPtr<nsIDocShell> parent;
8849 mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent));
8850
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 }
8856
8857 return mFrameElement;
8858 }
8859
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);
8871
8872 return rv.ErrorCode();
8873 }
8874
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.
8878
8879 void
8880 ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult)
8881 {
8882 nsAString::const_iterator end;
8883 aOptions.EndReading(end);
8884
8885 nsAString::const_iterator iter;
8886 aOptions.BeginReading(iter);
8887
8888 while (iter != end) {
8889 // Skip whitespace.
8890 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8891 ++iter;
8892 }
8893
8894 nsAString::const_iterator name_start = iter;
8895
8896 // Skip characters until we find whitespace, ';', ':', or '='
8897 while (iter != end && !nsCRT::IsAsciiSpace(*iter) &&
8898 *iter != ';' &&
8899 *iter != ':' &&
8900 *iter != '=') {
8901 ++iter;
8902 }
8903
8904 nsAString::const_iterator name_end = iter;
8905
8906 // Skip whitespace.
8907 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8908 ++iter;
8909 }
8910
8911 if (*iter == ';') {
8912 // No value found, skip the ';' and keep going.
8913 ++iter;
8914
8915 continue;
8916 }
8917
8918 nsAString::const_iterator value_start = iter;
8919 nsAString::const_iterator value_end = iter;
8920
8921 if (*iter == ':' || *iter == '=') {
8922 // We found name followed by ':' or '='. Look for a value.
8923
8924 iter++; // Skip the ':' or '='
8925
8926 // Skip whitespace.
8927 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8928 ++iter;
8929 }
8930
8931 value_start = iter;
8932
8933 // Skip until we find whitespace, or ';'.
8934 while (iter != end && !nsCRT::IsAsciiSpace(*iter) &&
8935 *iter != ';') {
8936 ++iter;
8937 }
8938
8939 value_end = iter;
8940
8941 // Skip whitespace.
8942 while (nsCRT::IsAsciiSpace(*iter) && iter != end) {
8943 ++iter;
8944 }
8945 }
8946
8947 const nsDependentSubstring& name = Substring(name_start, name_end);
8948 const nsDependentSubstring& value = Substring(value_start, value_end);
8949
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 }
8989
8990 if (iter == end) {
8991 break;
8992 }
8993
8994 iter++;
8995 }
8996 }
8997
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 }
9005
9006 FORWARD_TO_OUTER_OR_THROW(ShowModalDialog,
9007 (aUrl, aArgument, aOptions, aError), aError,
9008 nullptr);
9009
9010 if (Preferences::GetBool("dom.disable_window_showModalDialog", false)) {
9011 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9012 return nullptr;
9013 }
9014
9015 nsRefPtr<DialogValueHolder> argHolder =
9016 new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), aArgument);
9017
9018 // Before bringing up the window/dialog, unsuppress painting and flush
9019 // pending reflows.
9020 EnsureReflowFlushAndPaint();
9021
9022 if (!AreDialogsEnabled()) {
9023 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9024 return nullptr;
9025 }
9026
9027 if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
9028 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9029 return nullptr;
9030 }
9031
9032 nsCOMPtr<nsIDOMWindow> dlgWin;
9033 nsAutoString options(NS_LITERAL_STRING("-moz-internal-modal=1,status=1"));
9034
9035 ConvertDialogOptions(aOptions, options);
9036
9037 options.AppendLiteral(",scrollbars=1,centerscreen=1,resizable=0");
9038
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 }
9057
9058 nsCOMPtr<nsIDOMModalContentWindow> dialog = do_QueryInterface(dlgWin);
9059 if (!dialog) {
9060 return nullptr;
9061 }
9062
9063 nsCOMPtr<nsIVariant> retVal;
9064 aError = dialog->GetReturnValue(getter_AddRefs(retVal));
9065 MOZ_ASSERT(!aError.Failed());
9066
9067 return retVal.forget();
9068 }
9069
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));
9081
9082 nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aUrl, args, aOptions, aError);
9083 if (aError.Failed()) {
9084 return;
9085 }
9086
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 }
9096
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 }
9109
9110 ErrorResult rv;
9111 nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aURI, aArgs, aOptions, rv);
9112 retVal.forget(aRetVal);
9113
9114 return rv.ErrorCode();
9115 }
9116
9117 class CommandDispatcher : public nsRunnable
9118 {
9119 public:
9120 CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
9121 const nsAString& aAction)
9122 : mDispatcher(aDispatcher), mAction(aAction) {}
9123
9124 NS_IMETHOD Run()
9125 {
9126 return mDispatcher->UpdateCommands(mAction);
9127 }
9128
9129 nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
9130 nsString mAction;
9131 };
9132
9133 NS_IMETHODIMP
9134 nsGlobalWindow::UpdateCommands(const nsAString& anAction)
9135 {
9136 nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
9137 if (!rootWindow)
9138 return NS_OK;
9139
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 }
9152
9153 return NS_OK;
9154 }
9155
9156 Selection*
9157 nsGlobalWindow::GetSelection(ErrorResult& aError)
9158 {
9159 FORWARD_TO_OUTER_OR_THROW(GetSelection, (aError), aError, nullptr);
9160
9161 if (!mDocShell) {
9162 return nullptr;
9163 }
9164
9165 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
9166 if (!presShell) {
9167 return nullptr;
9168 }
9169
9170 return static_cast<Selection*>(presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL));
9171 }
9172
9173 NS_IMETHODIMP
9174 nsGlobalWindow::GetSelection(nsISelection** aSelection)
9175 {
9176 ErrorResult rv;
9177 nsCOMPtr<nsISelection> selection = GetSelection(rv);
9178 selection.forget(aSelection);
9179
9180 return rv.ErrorCode();
9181 }
9182
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 }
9193
9194 FORWARD_TO_OUTER_OR_THROW(Find,
9195 (aString, aCaseSensitive, aBackwards, aWrapAround,
9196 aWholeWord, aSearchInFrames, aShowDialog, aError),
9197 aError, false);
9198
9199 nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell));
9200 if (!finder) {
9201 aError.Throw(NS_ERROR_NOT_AVAILABLE);
9202 return false;
9203 }
9204
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);
9215
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 }
9225
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);
9231
9232 nsCOMPtr<nsIDOMWindow> findDialog;
9233
9234 if (windowMediator) {
9235 windowMediator->GetMostRecentWindow(MOZ_UTF16("findInPage"),
9236 getter_AddRefs(findDialog));
9237 }
9238
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 }
9250
9251 return false;
9252 }
9253
9254 // Launch the search with the passed in search string
9255 bool didFind = false;
9256 aError = finder->FindNext(&didFind);
9257 return didFind;
9258 }
9259
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);
9269
9270 return rv.ErrorCode();
9271 }
9272
9273 void
9274 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
9275 nsAString& aBinaryData, ErrorResult& aError)
9276 {
9277 aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData);
9278 }
9279
9280 NS_IMETHODIMP
9281 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
9282 nsAString& aBinaryData)
9283 {
9284 ErrorResult rv;
9285 Atob(aAsciiBase64String, aBinaryData, rv);
9286
9287 return rv.ErrorCode();
9288 }
9289
9290 void
9291 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
9292 nsAString& aAsciiBase64String, ErrorResult& aError)
9293 {
9294 aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
9295 }
9296
9297 NS_IMETHODIMP
9298 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
9299 nsAString& aAsciiBase64String)
9300 {
9301 ErrorResult rv;
9302 Btoa(aBinaryData, aAsciiBase64String, rv);
9303
9304 return rv.ErrorCode();
9305 }
9306
9307 //*****************************************************************************
9308 // nsGlobalWindow::nsIDOMEventTarget
9309 //*****************************************************************************
9310
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 }
9321
9322 NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsGlobalWindow)
9323
9324 NS_IMETHODIMP
9325 nsGlobalWindow::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal)
9326 {
9327 FORWARD_TO_INNER(DispatchEvent, (aEvent, aRetVal), NS_OK);
9328
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 }
9334
9335 if (!mDoc) {
9336 return NS_ERROR_FAILURE;
9337 }
9338
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 }
9346
9347 nsEventStatus status = nsEventStatus_eIgnore;
9348 nsresult rv =
9349 EventDispatcher::DispatchDOMEvent(GetOuterWindow(), nullptr, aEvent,
9350 presContext, &status);
9351
9352 *aRetVal = (status != nsEventStatus_eConsumeNoDefault);
9353 return rv;
9354 }
9355
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.");
9366
9367 if (IsOuterWindow() && mInnerWindow &&
9368 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9369 return NS_ERROR_DOM_SECURITY_ERR;
9370 }
9371
9372 if (!aWantsUntrusted &&
9373 (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
9374 aWantsUntrusted = true;
9375 }
9376
9377 EventListenerManager* manager = GetOrCreateListenerManager();
9378 NS_ENSURE_STATE(manager);
9379 manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted);
9380 return NS_OK;
9381 }
9382
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 }
9395
9396 bool wantsUntrusted;
9397 if (aWantsUntrusted.IsNull()) {
9398 wantsUntrusted = !nsContentUtils::IsChromeDoc(mDoc);
9399 } else {
9400 wantsUntrusted = aWantsUntrusted.Value();
9401 }
9402
9403 EventListenerManager* manager = GetOrCreateListenerManager();
9404 if (!manager) {
9405 aRv.Throw(NS_ERROR_UNEXPECTED);
9406 return;
9407 }
9408 manager->AddEventListener(aType, aListener, aUseCapture, wantsUntrusted);
9409 }
9410
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.");
9422
9423 if (IsOuterWindow() && mInnerWindow &&
9424 !nsContentUtils::CanCallerAccess(mInnerWindow)) {
9425 return NS_ERROR_DOM_SECURITY_ERR;
9426 }
9427
9428 if (!aWantsUntrusted &&
9429 (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
9430 aWantsUntrusted = true;
9431 }
9432
9433 return NS_AddSystemEventListener(this, aType, aListener, aUseCapture,
9434 aWantsUntrusted);
9435 }
9436
9437 EventListenerManager*
9438 nsGlobalWindow::GetOrCreateListenerManager()
9439 {
9440 FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr);
9441
9442 if (!mListenerManager) {
9443 mListenerManager =
9444 new EventListenerManager(static_cast<EventTarget*>(this));
9445 }
9446
9447 return mListenerManager;
9448 }
9449
9450 EventListenerManager*
9451 nsGlobalWindow::GetExistingListenerManager() const
9452 {
9453 FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr);
9454
9455 return mListenerManager;
9456 }
9457
9458 nsIScriptContext*
9459 nsGlobalWindow::GetContextForEventHandlers(nsresult* aRv)
9460 {
9461 *aRv = NS_ERROR_UNEXPECTED;
9462 NS_ENSURE_TRUE(!IsInnerWindow() || IsCurrentInnerWindow(), nullptr);
9463
9464 nsIScriptContext* scx;
9465 if ((scx = GetContext())) {
9466 *aRv = NS_OK;
9467 return scx;
9468 }
9469 return nullptr;
9470 }
9471
9472 //*****************************************************************************
9473 // nsGlobalWindow::nsPIDOMWindow
9474 //*****************************************************************************
9475
9476 nsPIDOMWindow*
9477 nsGlobalWindow::GetPrivateParent()
9478 {
9479 MOZ_ASSERT(IsOuterWindow());
9480
9481 nsCOMPtr<nsIDOMWindow> parent;
9482 GetParent(getter_AddRefs(parent));
9483
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.
9488
9489 nsIDocument* doc = chromeElement->GetDocument();
9490 if (!doc)
9491 return nullptr; // This is ok, just means a null parent.
9492
9493 return doc->GetWindow();
9494 }
9495
9496 if (parent) {
9497 return static_cast<nsGlobalWindow *>
9498 (static_cast<nsIDOMWindow*>(parent.get()));
9499 }
9500
9501 return nullptr;
9502 }
9503
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 }
9515
9516 nsCOMPtr<nsIDOMWindow> top;
9517 GetTop(getter_AddRefs(top));
9518
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 }
9529
9530 return static_cast<nsGlobalWindow*>(top.get());
9531 }
9532
9533
9534 nsIDOMLocation*
9535 nsGlobalWindow::GetLocation(ErrorResult& aError)
9536 {
9537 FORWARD_TO_INNER_OR_THROW(GetLocation, (aError), aError, nullptr);
9538
9539 nsIDocShell *docShell = GetDocShell();
9540 if (!mLocation && docShell) {
9541 mLocation = new nsLocation(docShell);
9542 }
9543 return mLocation;
9544 }
9545
9546 NS_IMETHODIMP
9547 nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation)
9548 {
9549 ErrorResult rv;
9550 nsCOMPtr<nsIDOMLocation> location = GetLocation(rv);
9551 location.forget(aLocation);
9552
9553 return rv.ErrorCode();
9554 }
9555
9556 void
9557 nsGlobalWindow::ActivateOrDeactivate(bool aActivate)
9558 {
9559 MOZ_ASSERT(IsOuterWindow());
9560
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;
9566
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 }
9573
9574 nsCOMPtr<nsPIDOMWindow> piMainWindow(
9575 do_QueryInterface(static_cast<nsIDOMWindow*>(this)));
9576 piMainWindow->SetActive(aActivate);
9577
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.
9584
9585 // Get the top level widget's nsGlobalWindow
9586 nsCOMPtr<nsIDOMWindow> topLevelWindow;
9587
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 }
9595
9596 if (topLevelWindow) {
9597 nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(topLevelWindow));
9598 piWin->SetActive(aActivate);
9599 }
9600 }
9601 }
9602
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 }
9610
9611 void
9612 nsGlobalWindow::SetActive(bool aActive)
9613 {
9614 nsPIDOMWindow::SetActive(aActive);
9615 NotifyDocumentTree(mDoc, nullptr);
9616 }
9617
9618 void nsGlobalWindow::SetIsBackground(bool aIsBackground)
9619 {
9620 MOZ_ASSERT(IsOuterWindow());
9621
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 }
9636
9637 void nsGlobalWindow::MaybeUpdateTouchState()
9638 {
9639 FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
9640
9641 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9642
9643 nsCOMPtr<nsIDOMWindow> focusedWindow;
9644 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
9645
9646 if(this == focusedWindow) {
9647 UpdateTouchState();
9648 }
9649
9650 if (mMayHaveTouchEventListener) {
9651 nsCOMPtr<nsIObserverService> observerService =
9652 services::GetObserverService();
9653
9654 if (observerService) {
9655 observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this),
9656 DOM_TOUCH_LISTENER_ADDED,
9657 nullptr);
9658 }
9659 }
9660 }
9661
9662 void nsGlobalWindow::UpdateTouchState()
9663 {
9664 FORWARD_TO_INNER_VOID(UpdateTouchState, ());
9665
9666 nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
9667 if (!mainWidget) {
9668 return;
9669 }
9670
9671 if (mMayHaveTouchEventListener) {
9672 mainWidget->RegisterTouchWindow();
9673 } else {
9674 mainWidget->UnregisterTouchWindow();
9675 }
9676 }
9677
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 }
9691
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 }
9705
9706 void
9707 nsGlobalWindow::SetChromeEventHandler(EventTarget* aChromeEventHandler)
9708 {
9709 MOZ_ASSERT(IsOuterWindow());
9710
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 }
9721
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 }
9728
9729 void
9730 nsGlobalWindow::SetFocusedNode(nsIContent* aNode,
9731 uint32_t aFocusMethod,
9732 bool aNeedsFocus)
9733 {
9734 FORWARD_TO_INNER_VOID(SetFocusedNode, (aNode, aFocusMethod, aNeedsFocus));
9735
9736 if (aNode && aNode->GetCurrentDoc() != mDoc) {
9737 NS_WARNING("Trying to set focus to a node from a wrong document");
9738 return;
9739 }
9740
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 }
9752
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 }
9770
9771 if (aNeedsFocus)
9772 mNeedsFocus = aNeedsFocus;
9773 }
9774
9775 uint32_t
9776 nsGlobalWindow::GetFocusMethod()
9777 {
9778 FORWARD_TO_INNER(GetFocusMethod, (), 0);
9779
9780 return mFocusMethod;
9781 }
9782
9783 bool
9784 nsGlobalWindow::ShouldShowFocusRing()
9785 {
9786 FORWARD_TO_INNER(ShouldShowFocusRing, (), false);
9787
9788 return mShowFocusRings || mShowFocusRingForContent || mFocusByKeyOccurred;
9789 }
9790
9791 void
9792 nsGlobalWindow::SetKeyboardIndicators(UIStateChangeType aShowAccelerators,
9793 UIStateChangeType aShowFocusRings)
9794 {
9795 FORWARD_TO_INNER_VOID(SetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
9796
9797 bool oldShouldShowFocusRing = ShouldShowFocusRing();
9798
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;
9804
9805 // propagate the indicators to child windows
9806 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
9807 if (docShell) {
9808 int32_t childCount = 0;
9809 docShell->GetChildCount(&childCount);
9810
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 }
9820
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 }
9833
9834 void
9835 nsGlobalWindow::GetKeyboardIndicators(bool* aShowAccelerators,
9836 bool* aShowFocusRings)
9837 {
9838 FORWARD_TO_INNER_VOID(GetKeyboardIndicators, (aShowAccelerators, aShowFocusRings));
9839
9840 *aShowAccelerators = mShowAccelerators;
9841 *aShowFocusRings = mShowFocusRings;
9842 }
9843
9844 bool
9845 nsGlobalWindow::TakeFocus(bool aFocus, uint32_t aFocusMethod)
9846 {
9847 FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false);
9848
9849 if (mCleanedUp) {
9850 return false;
9851 }
9852
9853 if (aFocus)
9854 mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
9855
9856 if (mHasFocus != aFocus) {
9857 mHasFocus = aFocus;
9858 UpdateCanvasFocus(true, mFocusedNode);
9859 }
9860
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 }
9870
9871 mNeedsFocus = false;
9872 return false;
9873 }
9874
9875 void
9876 nsGlobalWindow::SetReadyForFocus()
9877 {
9878 FORWARD_TO_INNER_VOID(SetReadyForFocus, ());
9879
9880 bool oldNeedsFocus = mNeedsFocus;
9881 mNeedsFocus = false;
9882
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 }
9891
9892 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9893 if (fm)
9894 fm->WindowShown(this, oldNeedsFocus);
9895 }
9896
9897 void
9898 nsGlobalWindow::PageHidden()
9899 {
9900 FORWARD_TO_INNER_VOID(PageHidden, ());
9901
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.
9905
9906 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9907 if (fm)
9908 fm->WindowHidden(this);
9909
9910 mNeedsFocus = true;
9911 }
9912
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 }
9926
9927 NS_IMETHOD Run()
9928 {
9929 NS_PRECONDITION(NS_IsMainThread(), "Should be called on the main thread.");
9930 return mWindow->FireHashchange(mOldURL, mNewURL);
9931 }
9932
9933 private:
9934 nsString mOldURL;
9935 nsString mNewURL;
9936 nsRefPtr<nsGlobalWindow> mWindow;
9937 };
9938
9939 nsresult
9940 nsGlobalWindow::DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI)
9941 {
9942 FORWARD_TO_INNER(DispatchAsyncHashchange, (aOldURI, aNewURI), NS_OK);
9943
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);
9949
9950 NS_ENSURE_STATE(oldBeforeHash.Equals(newBeforeHash));
9951 NS_ENSURE_STATE(!oldHash.Equals(newHash));
9952
9953 nsAutoCString oldSpec, newSpec;
9954 aOldURI->GetSpec(oldSpec);
9955 aNewURI->GetSpec(newSpec);
9956
9957 NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
9958 NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
9959
9960 nsCOMPtr<nsIRunnable> callback =
9961 new HashchangeCallback(oldWideSpec, newWideSpec, this);
9962 return NS_DispatchToMainThread(callback);
9963 }
9964
9965 nsresult
9966 nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
9967 const nsAString &aNewURL)
9968 {
9969 MOZ_ASSERT(IsInnerWindow());
9970
9971 // Don't do anything if the window is frozen.
9972 if (IsFrozen())
9973 return NS_OK;
9974
9975 // Get a presentation shell for use in creating the hashchange event.
9976 NS_ENSURE_STATE(IsCurrentInnerWindow());
9977
9978 nsIPresShell *shell = mDoc->GetShell();
9979 nsRefPtr<nsPresContext> presContext;
9980 if (shell) {
9981 presContext = shell->GetPresContext();
9982 }
9983
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);
9991
9992 nsCOMPtr<nsIDOMHashChangeEvent> hashchangeEvent = do_QueryInterface(domEvent);
9993 NS_ENSURE_TRUE(hashchangeEvent, NS_ERROR_UNEXPECTED);
9994
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);
10000
10001 domEvent->SetTrusted(true);
10002
10003 bool dummy;
10004 return DispatchEvent(hashchangeEvent, &dummy);
10005 }
10006
10007 nsresult
10008 nsGlobalWindow::DispatchSyncPopState()
10009 {
10010 FORWARD_TO_INNER(DispatchSyncPopState, (), NS_OK);
10011
10012 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
10013 "Must be safe to run script here.");
10014
10015 // Check that PopState hasn't been pref'ed off.
10016 if (!Preferences::GetBool(sPopStatePrefStr, false)) {
10017 return NS_OK;
10018 }
10019
10020 nsresult rv = NS_OK;
10021
10022 // Bail if the window is frozen.
10023 if (IsFrozen()) {
10024 return NS_OK;
10025 }
10026
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);
10033
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 }
10040
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);
10047
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);
10054
10055 domEvent->SetTrusted(true);
10056
10057 nsCOMPtr<EventTarget> outerWindow =
10058 do_QueryInterface(GetOuterWindow());
10059 NS_ENSURE_TRUE(outerWindow, NS_ERROR_UNEXPECTED);
10060
10061 rv = domEvent->SetTarget(outerWindow);
10062 NS_ENSURE_SUCCESS(rv, rv);
10063
10064 bool dummy; // default action
10065 return DispatchEvent(popstateEvent, &dummy);
10066 }
10067
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 }
10076
10077 nsIFrame* kid = aFrame->GetFirstPrincipalChild();
10078 while (kid) {
10079 canvasFrame = FindCanvasFrame(kid);
10080 if (canvasFrame) {
10081 return canvasFrame;
10082 }
10083 kid = kid->GetNextSibling();
10084 }
10085
10086 return nullptr;
10087 }
10088
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());
10095
10096 // this is called from the inner window so use GetDocShell
10097 nsIDocShell* docShell = GetDocShell();
10098 if (!docShell)
10099 return;
10100
10101 bool editable;
10102 docShell->GetEditable(&editable);
10103 if (editable)
10104 return;
10105
10106 nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
10107 if (!presShell || !mDoc)
10108 return;
10109
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 }
10134
10135 already_AddRefed<nsICSSDeclaration>
10136 nsGlobalWindow::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt,
10137 ErrorResult& aError)
10138 {
10139 return GetComputedStyleHelper(aElt, aPseudoElt, false, aError);
10140 }
10141
10142 NS_IMETHODIMP
10143 nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt,
10144 const nsAString& aPseudoElt,
10145 nsIDOMCSSStyleDeclaration** aReturn)
10146 {
10147 return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn);
10148 }
10149
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 }
10157
10158 NS_IMETHODIMP
10159 nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt,
10160 const nsAString& aPseudoElt,
10161 nsIDOMCSSStyleDeclaration** aReturn)
10162 {
10163 return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn);
10164 }
10165
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;
10174
10175 nsCOMPtr<dom::Element> element = do_QueryInterface(aElt);
10176 if (!element) {
10177 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
10178 }
10179
10180 ErrorResult rv;
10181 nsCOMPtr<nsIDOMCSSStyleDeclaration> declaration =
10182 GetComputedStyleHelper(*element, aPseudoElt, aDefaultStylesOnly, rv);
10183 declaration.forget(aReturn);
10184
10185 return rv.ErrorCode();
10186 }
10187
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);
10197
10198 if (!mDocShell) {
10199 return nullptr;
10200 }
10201
10202 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
10203
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 }
10212
10213 parent->FlushPendingNotifications(Flush_Frames);
10214
10215 // Might have killed mDocShell
10216 if (!mDocShell) {
10217 return nullptr;
10218 }
10219
10220 presShell = mDocShell->GetPresShell();
10221 if (!presShell) {
10222 return nullptr;
10223 }
10224 }
10225
10226 nsRefPtr<nsComputedDOMStyle> compStyle =
10227 NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell,
10228 aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly :
10229 nsComputedDOMStyle::eAll);
10230
10231 return compStyle.forget();
10232 }
10233
10234 nsIDOMStorage*
10235 nsGlobalWindow::GetSessionStorage(ErrorResult& aError)
10236 {
10237 FORWARD_TO_INNER_OR_THROW(GetSessionStorage, (aError), aError, nullptr);
10238
10239 nsIPrincipal *principal = GetPrincipal();
10240 nsIDocShell* docShell = GetDocShell();
10241
10242 if (!principal || !docShell || !Preferences::GetBool(kStorageEnabled)) {
10243 return nullptr;
10244 }
10245
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 }
10263
10264 if (!mSessionStorage) {
10265 nsString documentURI;
10266 if (mDoc) {
10267 mDoc->GetDocumentURI(documentURI);
10268 }
10269
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 }
10276
10277 if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) {
10278 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10279 return nullptr;
10280 }
10281
10282 nsresult rv;
10283
10284 nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
10285 if (NS_FAILED(rv)) {
10286 aError.Throw(rv);
10287 return nullptr;
10288 }
10289
10290 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
10291
10292 nsCOMPtr<nsIURI> firstPartyIsolationURI;
10293 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
10294 if (NS_FAILED(rv)) {
10295 aError.Throw(rv);
10296 return nullptr;
10297 }
10298
10299 aError = storageManager->CreateStorageForFirstParty(firstPartyIsolationURI, principal,
10300 documentURI,
10301 loadContext && loadContext->UsePrivateBrowsing(),
10302 getter_AddRefs(mSessionStorage));
10303 if (aError.Failed()) {
10304 return nullptr;
10305 }
10306
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
10312
10313 if (!mSessionStorage) {
10314 aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10315 return nullptr;
10316 }
10317 }
10318
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
10324
10325 return mSessionStorage;
10326 }
10327
10328 NS_IMETHODIMP
10329 nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage)
10330 {
10331 ErrorResult rv;
10332 nsCOMPtr<nsIDOMStorage> storage = GetSessionStorage(rv);
10333 storage.forget(aSessionStorage);
10334
10335 return rv.ErrorCode();
10336 }
10337
10338 nsIDOMStorage*
10339 nsGlobalWindow::GetLocalStorage(ErrorResult& aError)
10340 {
10341 FORWARD_TO_INNER_OR_THROW(GetLocalStorage, (aError), aError, nullptr);
10342
10343 if (!Preferences::GetBool(kStorageEnabled)) {
10344 return nullptr;
10345 }
10346
10347 if (!mLocalStorage) {
10348 if (!DOMStorage::CanUseStorage()) {
10349 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
10350 return nullptr;
10351 }
10352
10353 nsIPrincipal *principal = GetPrincipal();
10354 if (!principal) {
10355 return nullptr;
10356 }
10357
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 }
10365
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 }
10372
10373 nsString documentURI;
10374 if (mDoc) {
10375 mDoc->GetDocumentURI(documentURI);
10376 }
10377
10378 nsCOMPtr<nsIURI> firstPartyIsolationURI;
10379 rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI));
10380 if (NS_FAILED(rv)) {
10381 aError.Throw(rv);
10382 return nullptr;
10383 }
10384
10385 nsIDocShell* docShell = GetDocShell();
10386 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
10387
10388 aError = storageManager->CreateStorageForFirstParty(firstPartyIsolationURI, principal,
10389 documentURI,
10390 loadContext && loadContext->UsePrivateBrowsing(),
10391 getter_AddRefs(mLocalStorage));
10392 }
10393
10394 return mLocalStorage;
10395 }
10396
10397 NS_IMETHODIMP
10398 nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage)
10399 {
10400 NS_ENSURE_ARG(aLocalStorage);
10401
10402 ErrorResult rv;
10403 nsCOMPtr<nsIDOMStorage> storage = GetLocalStorage(rv);
10404 storage.forget(aLocalStorage);
10405
10406 return rv.ErrorCode();
10407 }
10408
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 }
10419
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 }
10436
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 }
10444
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 }
10455
10456 // This may be null if being created from a file.
10457 aError = indexedDB::IDBFactory::Create(this, nullptr,
10458 getter_AddRefs(mIndexedDB));
10459 }
10460
10461 return mIndexedDB;
10462 }
10463
10464 NS_IMETHODIMP
10465 nsGlobalWindow::GetIndexedDB(nsISupports** _retval)
10466 {
10467 ErrorResult rv;
10468 nsCOMPtr<nsISupports> request(GetIndexedDB(rv));
10469 request.forget(_retval);
10470
10471 return rv.ErrorCode();
10472 }
10473
10474 NS_IMETHODIMP
10475 nsGlobalWindow::GetMozIndexedDB(nsISupports** _retval)
10476 {
10477 return GetIndexedDB(_retval);
10478 }
10479
10480 //*****************************************************************************
10481 // nsGlobalWindow::nsIInterfaceRequestor
10482 //*****************************************************************************
10483
10484 NS_IMETHODIMP
10485 nsGlobalWindow::GetInterface(const nsIID & aIID, void **aSink)
10486 {
10487 NS_ENSURE_ARG_POINTER(aSink);
10488 *aSink = nullptr;
10489
10490 if (aIID.Equals(NS_GET_IID(nsIDocCharset))) {
10491 nsGlobalWindow* outer = GetOuterWindowInternal();
10492 NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED);
10493
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);
10501
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);
10508
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);
10516
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);
10530
10531 if (!mWindowUtils) {
10532 mWindowUtils = new nsDOMWindowUtils(outer);
10533 }
10534
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);
10541
10542 nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(outer->mDocShell));
10543 loadContext.forget(aSink);
10544 }
10545 else {
10546 return QueryInterface(aIID, aSink);
10547 }
10548
10549 return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
10550 }
10551
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 }
10559
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 }
10588
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 { }
10599
10600 NS_IMETHOD Run()
10601 {
10602 if (mIdleWindow->ContainsIdleObserver(mIdleObserver, mTimeInS)) {
10603 return mCallOnidle ? mIdleObserver->Onidle() : mIdleObserver->Onactive();
10604 }
10605 return NS_OK;
10606 }
10607
10608 private:
10609 nsCOMPtr<nsIIdleObserver> mIdleObserver;
10610 uint32_t mTimeInS;
10611 nsRefPtr<nsGlobalWindow> mIdleWindow;
10612
10613 // If false then call on active
10614 bool mCallOnidle;
10615 };
10616
10617 void
10618 nsGlobalWindow::NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
10619 bool aCallOnidle)
10620 {
10621 MOZ_ASSERT(aIdleObserverHolder);
10622 aIdleObserverHolder->mPrevNotificationIdle = aCallOnidle;
10623
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 }
10632
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 }
10649
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 }
10657
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 }
10665
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 }
10679
10680 nsresult
10681 nsGlobalWindow::ScheduleNextIdleObserverCallback()
10682 {
10683 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10684 MOZ_ASSERT(mIdleService, "No idle service!");
10685
10686 if (mIdleCallbackIndex < 0 ||
10687 static_cast<uint32_t>(mIdleCallbackIndex) >= mIdleObservers.Length()) {
10688 return NS_OK;
10689 }
10690
10691 IdleObserverHolder& idleObserver =
10692 mIdleObservers.ElementAt(mIdleCallbackIndex);
10693
10694 uint32_t userIdleTimeMS = 0;
10695 nsresult rv = mIdleService->GetIdleTime(&userIdleTimeMS);
10696 NS_ENSURE_SUCCESS(rv, rv);
10697
10698 uint32_t callbackTimeMS = 0;
10699 if (idleObserver.mTimeInS * 1000 + mIdleFuzzFactor > userIdleTimeMS) {
10700 callbackTimeMS = idleObserver.mTimeInS * 1000 - userIdleTimeMS + mIdleFuzzFactor;
10701 }
10702
10703 mIdleTimer->Cancel();
10704 rv = mIdleTimer->InitWithFuncCallback(IdleObserverTimerCallback,
10705 this,
10706 callbackTimeMS,
10707 nsITimer::TYPE_ONE_SHOT);
10708 NS_ENSURE_SUCCESS(rv, rv);
10709
10710 return NS_OK;
10711 }
10712
10713 uint32_t
10714 nsGlobalWindow::GetFuzzTimeMS()
10715 {
10716 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10717
10718 if (sIdleObserversAPIFuzzTimeDisabled) {
10719 return 0;
10720 }
10721
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 }
10728
10729 if (randNum > MAX_IDLE_FUZZ_TIME_MS) {
10730 randNum %= MAX_IDLE_FUZZ_TIME_MS;
10731 }
10732
10733 return randNum;
10734 }
10735
10736 nsresult
10737 nsGlobalWindow::ScheduleActiveTimerCallback()
10738 {
10739 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10740
10741 if (!mAddActiveEventFuzzTime) {
10742 return HandleIdleActiveEvent();
10743 }
10744
10745 MOZ_ASSERT(mIdleTimer);
10746 mIdleTimer->Cancel();
10747
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 }
10756
10757 nsresult
10758 nsGlobalWindow::HandleIdleActiveEvent()
10759 {
10760 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10761
10762 if (mCurrentlyIdle) {
10763 mIdleCallbackIndex = 0;
10764 mIdleFuzzFactor = GetFuzzTimeMS();
10765 nsresult rv = ScheduleNextIdleObserverCallback();
10766 NS_ENSURE_SUCCESS(rv, rv);
10767 return NS_OK;
10768 }
10769
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 }
10780
10781 return NS_OK;
10782 }
10783
10784 nsGlobalWindow::SlowScriptResponse
10785 nsGlobalWindow::ShowSlowScriptDialog()
10786 {
10787 MOZ_ASSERT(IsInnerWindow());
10788
10789 nsresult rv;
10790 AutoJSContext cx;
10791
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 }
10799
10800 // If our document is not active, just kill the script: we've been unloaded
10801 if (!HasActiveDocument()) {
10802 return KillSlowScript;
10803 }
10804
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);
10810
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);
10815
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);
10824
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 }
10830
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
10837
10838 // Get localizable strings
10839 nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
10840
10841 rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10842 "KillScriptTitle",
10843 title);
10844
10845 nsresult tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10846 "StopScriptButton",
10847 stopButton);
10848 if (NS_FAILED(tmp)) {
10849 rv = tmp;
10850 }
10851
10852 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10853 "WaitForScriptButton",
10854 waitButton);
10855 if (NS_FAILED(tmp)) {
10856 rv = tmp;
10857 }
10858
10859 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10860 "DontAskAgain",
10861 neverShowDlg);
10862 if (NS_FAILED(tmp)) {
10863 rv = tmp;
10864 }
10865
10866
10867 if (debugPossible) {
10868 tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
10869 "DebugScriptButton",
10870 debugButton);
10871 if (NS_FAILED(tmp)) {
10872 rv = tmp;
10873 }
10874
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 }
10890
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 }
10897
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);
10907
10908 if (NS_SUCCEEDED(rv) && scriptLocation) {
10909 msg.AppendLiteral("\n\n");
10910 msg.Append(scriptLocation);
10911 msg.Append(':');
10912 msg.AppendInt(lineno);
10913 }
10914 }
10915
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));
10921
10922 // Add a third button if necessary.
10923 if (debugPossible)
10924 buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
10925
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);
10929
10930 // Open the dialog.
10931 rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
10932 debugButton, neverShowDlg, &neverShowDlgChk,
10933 &buttonPressed);
10934
10935 JS_SetInterruptCallback(rt, old);
10936
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 }
10946
10947 uint32_t
10948 nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver)
10949 {
10950 MOZ_ASSERT(IsInnerWindow());
10951 MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
10952
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 }
10963
10964 return i;
10965 }
10966
10967 nsresult
10968 nsGlobalWindow::RegisterIdleObserver(nsIIdleObserver* aIdleObserver)
10969 {
10970 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
10971
10972 nsresult rv;
10973 if (mIdleObservers.IsEmpty()) {
10974 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1", &rv);
10975 NS_ENSURE_SUCCESS(rv, rv);
10976
10977 rv = mIdleService->AddIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
10978 NS_ENSURE_SUCCESS(rv, rv);
10979
10980 if (!mIdleTimer) {
10981 mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
10982 NS_ENSURE_SUCCESS(rv, rv);
10983 } else {
10984 mIdleTimer->Cancel();
10985 }
10986 }
10987
10988 MOZ_ASSERT(mIdleService);
10989 MOZ_ASSERT(mIdleTimer);
10990
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);
10997
10998 uint32_t insertAtIndex = FindInsertionIndex(&tmpIdleObserver);
10999 if (insertAtIndex == mIdleObservers.Length()) {
11000 mIdleObservers.AppendElement(tmpIdleObserver);
11001 }
11002 else {
11003 mIdleObservers.InsertElementAt(insertAtIndex, tmpIdleObserver);
11004 }
11005
11006 bool userIsIdle = false;
11007 rv = nsContentUtils::IsUserIdle(MIN_IDLE_NOTIFICATION_TIME_S, &userIsIdle);
11008 NS_ENSURE_SUCCESS(rv, rv);
11009
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 }
11016
11017 if (!mCurrentlyIdle) {
11018 return NS_OK;
11019 }
11020
11021 MOZ_ASSERT(mIdleCallbackIndex >= 0);
11022
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 }
11029
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 }
11037
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.");
11044
11045 *aRemoveElementIndex = 0;
11046 if (mIdleObservers.IsEmpty()) {
11047 return NS_ERROR_FAILURE;
11048 }
11049
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);
11054
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 }
11067
11068 nsresult
11069 nsGlobalWindow::UnregisterIdleObserver(nsIIdleObserver* aIdleObserver)
11070 {
11071 MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!");
11072
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);
11080
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;
11086
11087 mIdleTimer->Cancel();
11088 mIdleCallbackIndex = -1;
11089 return NS_OK;
11090 }
11091
11092 if (!mCurrentlyIdle) {
11093 return NS_OK;
11094 }
11095
11096 if (removeElementIndex < mIdleCallbackIndex) {
11097 mIdleCallbackIndex--;
11098 return NS_OK;
11099 }
11100
11101 if (removeElementIndex != mIdleCallbackIndex) {
11102 return NS_OK;
11103 }
11104
11105 mIdleTimer->Cancel();
11106
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);
11120
11121 return NS_OK;
11122 }
11123
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 }
11138
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 }
11150
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 }
11162
11163 if (!nsCRT::strcmp(aTopic, "dom-storage2-changed")) {
11164 if (!IsInnerWindow() || !IsCurrentInnerWindow()) {
11165 return NS_OK;
11166 }
11167
11168 nsIPrincipal *principal;
11169 nsresult rv;
11170
11171 nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv);
11172 NS_ENSURE_SUCCESS(rv, rv);
11173
11174 nsCOMPtr<nsIDOMStorage> changingStorage;
11175 rv = event->GetStorageArea(getter_AddRefs(changingStorage));
11176 NS_ENSURE_SUCCESS(rv, rv);
11177
11178 bool fireMozStorageChanged = false;
11179 principal = GetPrincipal();
11180 if (!principal) {
11181 return NS_OK;
11182 }
11183
11184 nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage);
11185
11186 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell());
11187 bool isPrivate = loadContext && loadContext->UsePrivateBrowsing();
11188 if (pistorage->IsPrivate() != isPrivate) {
11189 return NS_OK;
11190 }
11191
11192 switch (pistorage->GetType())
11193 {
11194 case nsPIDOMStorage::SessionStorage:
11195 {
11196 bool check = false;
11197
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);
11204
11205 rv = storageManager->CheckStorageForFirstParty(firstPartyIsolationURI,
11206 principal, changingStorage, &check);
11207 if (NS_FAILED(rv)) {
11208 return rv;
11209 }
11210 }
11211
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 }
11217
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
11223
11224 fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage);
11225 break;
11226 }
11227
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();
11233
11234 bool equals = false;
11235 rv = storagePrincipal->Equals(principal, &equals);
11236 NS_ENSURE_SUCCESS(rv, rv);
11237
11238 if (!equals)
11239 return NS_OK;
11240
11241 fireMozStorageChanged = SameCOMIdentity(mLocalStorage, changingStorage);
11242 break;
11243 }
11244 default:
11245 return NS_OK;
11246 }
11247
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);
11255
11256 event->SetTrusted(true);
11257
11258 if (fireMozStorageChanged) {
11259 WidgetEvent* internalEvent = event->GetInternalNSEvent();
11260 internalEvent->mFlags.mOnlyChromeDispatch = true;
11261 }
11262
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.
11267
11268 mPendingStorageEvents.AppendObject(event);
11269 return NS_OK;
11270 }
11271
11272 bool defaultActionEnabled;
11273 DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled);
11274
11275 return NS_OK;
11276 }
11277
11278 if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
11279 if (mApplicationCache)
11280 return NS_OK;
11281
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);
11290
11291 return NS_OK;
11292 }
11293
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 }
11301
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);
11310
11311 event->SetTrusted(true);
11312
11313 bool dummy;
11314 return DispatchEvent(event, &dummy);
11315 }
11316 #endif // MOZ_B2G
11317
11318 NS_WARNING("unrecognized topic in nsGlobalWindow::Observe");
11319 return NS_ERROR_FAILURE;
11320 }
11321
11322 nsresult
11323 nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
11324 nsCOMPtr<nsIDOMStorageEvent>& aEvent)
11325 {
11326 nsresult rv;
11327
11328 bool canBubble;
11329 bool cancelable;
11330 nsAutoString key;
11331 nsAutoString oldValue;
11332 nsAutoString newValue;
11333 nsAutoString url;
11334 nsCOMPtr<nsIDOMStorage> storageArea;
11335
11336 nsCOMPtr<nsIDOMEvent> domEvent = do_QueryInterface(aEvent, &rv);
11337 NS_ENSURE_SUCCESS(rv, rv);
11338
11339 domEvent->GetBubbles(&canBubble);
11340 domEvent->GetCancelable(&cancelable);
11341
11342 aEvent->GetKey(key);
11343 aEvent->GetOldValue(oldValue);
11344 aEvent->GetNewValue(newValue);
11345 aEvent->GetUrl(url);
11346 aEvent->GetStorageArea(getter_AddRefs(storageArea));
11347
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 }
11354
11355 nsresult
11356 nsGlobalWindow::FireDelayedDOMEvents()
11357 {
11358 FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
11359
11360 for (int32_t i = 0; i < mPendingStorageEvents.Count(); ++i) {
11361 Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr);
11362 }
11363
11364 if (mApplicationCache) {
11365 static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
11366 }
11367
11368 if (mFireOfflineStatusChangeEventOnThaw) {
11369 mFireOfflineStatusChangeEventOnThaw = false;
11370 FireOfflineStatusEvent();
11371 }
11372
11373 if (mNotifyIdleObserversIdleOnThaw) {
11374 mNotifyIdleObserversIdleOnThaw = false;
11375 HandleIdleActiveEvent();
11376 }
11377
11378 if (mNotifyIdleObserversActiveOnThaw) {
11379 mNotifyIdleObserversActiveOnThaw = false;
11380 ScheduleActiveTimerCallback();
11381 }
11382
11383 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
11384 if (docShell) {
11385 int32_t childCount = 0;
11386 docShell->GetChildCount(&childCount);
11387
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");
11392
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 }
11402
11403 return NS_OK;
11404 }
11405
11406 //*****************************************************************************
11407 // nsGlobalWindow: Window Control Functions
11408 //*****************************************************************************
11409
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 }
11421
11422 nsCOMPtr<nsIDOMWindow> parent;
11423 GetParent(getter_AddRefs(parent));
11424
11425 if (parent && parent != static_cast<nsIDOMWindow *>(this)) {
11426 return parent;
11427 }
11428
11429 return nullptr;
11430 }
11431
11432 void
11433 nsGlobalWindow::UnblockScriptedClosing()
11434 {
11435 MOZ_ASSERT(IsOuterWindow());
11436 mBlockScriptedClosingFlag = false;
11437 }
11438
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 };
11456
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);
11473
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");
11485
11486 mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker;
11487
11488 // Calls to window.open from script should navigate.
11489 MOZ_ASSERT(aCalledNoScript || aNavigate);
11490
11491 *aReturn = nullptr;
11492
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 }
11499
11500 NS_ASSERTION(mDocShell, "Must have docshell here");
11501
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 }
11508
11509 const bool checkForPopup = !nsContentUtils::IsCallerChrome() &&
11510 !isApp && !aDialog && !WindowExists(aName, !aCalledNoScript);
11511
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;
11517
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);
11523
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 }
11533
11534 if (NS_FAILED(rv))
11535 return rv;
11536
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 }
11552
11553 FireAbuseEvents(true, false, aUrl, aName, aOptions);
11554 return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
11555 }
11556 }
11557
11558 nsCOMPtr<nsIDOMWindow> domReturn;
11559
11560 nsCOMPtr<nsIWindowWatcher> wwatch =
11561 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
11562 NS_ENSURE_TRUE(wwatch, rv);
11563
11564 NS_ConvertUTF16toUTF8 options(aOptions);
11565 NS_ConvertUTF16toUTF8 name(aName);
11566
11567 const char *options_ptr = aOptions.IsEmpty() ? nullptr : options.get();
11568 const char *name_ptr = aName.IsEmpty() ? nullptr : name.get();
11569
11570 nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
11571 NS_ENSURE_STATE(pwwatch);
11572
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);
11578
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.
11591
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 }
11600
11601
11602 rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
11603 /* aCalledFromScript = */ false,
11604 aDialog, aNavigate, aExtraArgument,
11605 getter_AddRefs(domReturn));
11606
11607 }
11608 }
11609
11610 NS_ENSURE_SUCCESS(rv, rv);
11611
11612 // success!
11613
11614 NS_ENSURE_TRUE(domReturn, NS_OK);
11615 domReturn.swap(*aReturn);
11616
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
11632
11633 nsCOMPtr<nsIDOMDocument> doc;
11634 (*aReturn)->GetDocument(getter_AddRefs(doc));
11635 }
11636 }
11637
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 }
11649
11650 return rv;
11651 }
11652
11653 //*****************************************************************************
11654 // nsGlobalWindow: Timeout Functions
11655 //*****************************************************************************
11656
11657 uint32_t sNestingLevel;
11658
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;
11667
11668 forwardTo = this;
11669 } else {
11670 currentInner = GetCurrentInnerWindowInternal();
11671
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().
11678
11679 forwardTo = CallerInnerWindow();
11680 if (!forwardTo) {
11681 aError.Throw(NS_ERROR_NOT_AVAILABLE);
11682 return nullptr;
11683 }
11684
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 }
11694
11695 return currentInner;
11696 }
11697 }
11698
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 }
11704
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 }
11713
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 }
11722
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 }
11730
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 }
11736
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 }
11748
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 }
11759
11760 nsresult
11761 nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
11762 int32_t interval,
11763 bool aIsInterval, int32_t *aReturn)
11764 {
11765 MOZ_ASSERT(IsInnerWindow());
11766
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 }
11772
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);
11776
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 }
11784
11785 nsRefPtr<nsTimeout> timeout = new nsTimeout();
11786 timeout->mIsInterval = aIsInterval;
11787 timeout->mInterval = interval;
11788 timeout->mScriptHandler = aHandler;
11789
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 }
11798
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.
11803
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 }
11811
11812 bool subsumes = false;
11813 nsCOMPtr<nsIPrincipal> ourPrincipal = GetPrincipal();
11814
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 }
11823
11824 if (subsumes) {
11825 timeout->mPrincipal = subjectPrincipal;
11826 } else {
11827 timeout->mPrincipal = ourPrincipal;
11828 }
11829
11830 ++gTimeoutsRecentlySet;
11831 TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
11832
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.
11837
11838 timeout->mWhen = TimeStamp::Now() + delta;
11839
11840 timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
11841 if (NS_FAILED(rv)) {
11842 return rv;
11843 }
11844
11845 nsRefPtr<nsTimeout> copy = timeout;
11846
11847 rv = timeout->InitTimer(TimerCallback, realInterval);
11848 if (NS_FAILED(rv)) {
11849 return rv;
11850 }
11851
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.
11860
11861 timeout->mTimeRemaining = delta;
11862 }
11863
11864 timeout->mWindow = this;
11865
11866 if (!aIsInterval) {
11867 timeout->mNestingLevel = nestingLevel;
11868 }
11869
11870 // No popups from timeouts by default
11871 timeout->mPopupState = openAbused;
11872
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).
11878
11879 int32_t delay =
11880 Preferences::GetInt("dom.disable_open_click_delay");
11881
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 }
11889
11890 InsertTimeoutIntoList(timeout);
11891
11892 timeout->mPublicId = ++mTimeoutPublicIdCounter;
11893 *aReturn = timeout->mPublicId;
11894
11895 return NS_OK;
11896
11897 }
11898
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().
11908
11909 if (IsOuterWindow()) {
11910 nsGlobalWindow* callerInner = CallerInnerWindow();
11911 NS_ENSURE_TRUE(callerInner, NS_ERROR_NOT_AVAILABLE);
11912
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).
11917
11918 if (callerInner->GetOuterWindow() == this &&
11919 callerInner->IsInnerWindow()) {
11920 return callerInner->SetTimeoutOrInterval(aIsInterval, aReturn);
11921 }
11922
11923 FORWARD_TO_INNER(SetTimeoutOrInterval, (aIsInterval, aReturn),
11924 NS_ERROR_NOT_INITIALIZED);
11925 }
11926
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 }
11938
11939 return SetTimeoutOrInterval(handler, interval, isInterval, aReturn);
11940 }
11941
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 }
11951
11952 if (inner != this) {
11953 return inner->SetTimeoutOrInterval(aFunction, aTimeout, aArguments,
11954 aIsInterval, aError);
11955 }
11956
11957 nsCOMPtr<nsIScriptTimeoutHandler> handler =
11958 NS_CreateJSTimeoutHandler(this, aFunction, aArguments, aError);
11959 if (!handler) {
11960 return 0;
11961 }
11962
11963 int32_t result;
11964 aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
11965 return result;
11966 }
11967
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 }
11977
11978 if (inner != this) {
11979 return inner->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
11980 aError);
11981 }
11982
11983 nsCOMPtr<nsIScriptTimeoutHandler> handler =
11984 NS_CreateJSTimeoutHandler(aCx, this, aHandler, aError);
11985 if (!handler) {
11986 return 0;
11987 }
11988
11989 int32_t result;
11990 aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result);
11991 return result;
11992 }
11993
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;
12004
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);
12010
12011 // Clear the timeout's popup state, if any, to prevent interval
12012 // timeouts from repeatedly opening poups.
12013 timeout->mPopupState = openAbused;
12014
12015 ++gRunningTimeoutDepth;
12016 ++mTimeoutFiringDepth;
12017
12018 bool trackNestingLevel = !timeout->mIsInterval;
12019 uint32_t nestingLevel;
12020 if (trackNestingLevel) {
12021 nestingLevel = sNestingLevel;
12022 sNestingLevel = timeout->mNestingLevel;
12023 }
12024
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!");
12031
12032 const char* filename = nullptr;
12033 uint32_t lineNo = 0;
12034 handler->GetLocation(&filename, &lineNo);
12035
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 }
12052
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.
12061
12062 if (trackNestingLevel) {
12063 sNestingLevel = nestingLevel;
12064 }
12065
12066 --mTimeoutFiringDepth;
12067 --gRunningTimeoutDepth;
12068
12069 mRunningTimeout = last_running_timeout;
12070 timeout->mRunning = false;
12071 return timeout->mCleared;
12072 }
12073
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 }
12089
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())));
12095
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 }
12105
12106 TimeStamp currentNow = TimeStamp::Now();
12107 TimeDuration delay = firingTime - currentNow;
12108
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 }
12115
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?");
12120
12121 aTimeout->mTimeRemaining = delay;
12122 return true;
12123 }
12124
12125 aTimeout->mWhen = currentNow + delay;
12126
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());
12130
12131 if (NS_FAILED(rv)) {
12132 NS_ERROR("Error initializing timer for DOM timeout!");
12133
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;
12142
12143 // Now that the OS timer no longer has a reference to the
12144 // timeout we need to drop that reference.
12145 aTimeout->Release();
12146
12147 return false;
12148 }
12149
12150 return true;
12151 }
12152
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 }
12161
12162 NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
12163 NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");
12164
12165 nsTimeout *nextTimeout;
12166 nsTimeout *last_expired_timeout, *last_insertion_point;
12167 uint32_t firingDepth = mTimeoutFiringDepth + 1;
12168
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);
12172
12173 // A native timer has gone off. See which of our timeouts need
12174 // servicing
12175 TimeStamp now = TimeStamp::Now();
12176 TimeStamp deadline;
12177
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.
12184
12185 deadline = aTimeout->mWhen;
12186 } else {
12187 deadline = now;
12188 }
12189
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 }
12204
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 }
12211
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 }
12221
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();
12232
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;
12237
12238 Telemetry::AutoCounter<Telemetry::DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT> timeoutsRan;
12239
12240 for (nsTimeout *timeout = mTimeouts.getFirst();
12241 timeout != dummy_timeout && !IsFrozen();
12242 timeout = nextTimeout) {
12243 nextTimeout = timeout->getNext();
12244
12245 if (timeout->mFiringDepth != firingDepth) {
12246 // We skip the timeout since it's on the list to run at another
12247 // depth.
12248
12249 continue;
12250 }
12251
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 }
12258
12259 // The timeout is on the list to run at this depth, go ahead and
12260 // process it.
12261
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();
12265
12266 if (!scx) {
12267 // No context means this window was closed or never properly
12268 // initialized for this language.
12269 continue;
12270 }
12271
12272 // This timeout is good to run
12273 ++timeoutsRan;
12274 bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
12275
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");
12282
12283 mTimeoutInsertionPoint = last_insertion_point;
12284
12285 return;
12286 }
12287
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);
12291
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();
12295
12296 timeout->remove();
12297
12298 if (needsReinsertion) {
12299 // Insert interval timeout onto list sorted in deadline order.
12300 // AddRefs timeout.
12301 InsertTimeoutIntoList(timeout);
12302 }
12303
12304 // Release the timeout struct since it's possibly out of the list
12305 timeout->Release();
12306 }
12307
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");
12312
12313 mTimeoutInsertionPoint = last_insertion_point;
12314 }
12315
12316 void
12317 nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID, ErrorResult& aError)
12318 {
12319 FORWARD_TO_INNER_OR_THROW(ClearTimeoutOrInterval, (aTimerID, aError),
12320 aError, );
12321
12322 uint32_t public_id = (uint32_t)aTimerID;
12323 nsTimeout *timeout;
12324
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();
12336
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 }
12348
12349 nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow()
12350 {
12351 FORWARD_TO_INNER(ResetTimersForNonBackgroundWindow, (),
12352 NS_ERROR_NOT_INITIALIZED);
12353
12354 if (IsFrozen() || mTimeoutsSuspendDepth) {
12355 return NS_OK;
12356 }
12357
12358 TimeStamp now = TimeStamp::Now();
12359
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 }
12377
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 }
12385
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);
12399
12400 NS_ASSERTION(firingTime < timeout->mWhen,
12401 "Our firing time should strictly decrease!");
12402
12403 TimeDuration delay = firingTime - now;
12404 timeout->mWhen = firingTime;
12405
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.
12408
12409 // Get the pointer to the next timeout now, before we move the
12410 // current timeout in the list.
12411 nsTimeout* nextTimeout = timeout->getNext();
12412
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();
12425
12426 nsresult rv = timeout->InitTimer(TimerCallback, delay.ToMilliseconds());
12427
12428 if (NS_FAILED(rv)) {
12429 NS_WARNING("Error resetting non background timer for DOM timeout!");
12430 return rv;
12431 }
12432
12433 timeout = nextTimeout;
12434 } else {
12435 timeout = timeout->getNext();
12436 }
12437 }
12438
12439 return NS_OK;
12440 }
12441
12442 void
12443 nsGlobalWindow::ClearAllTimeouts()
12444 {
12445 nsTimeout *timeout, *nextTimeout;
12446
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;
12455
12456 nextTimeout = timeout->getNext();
12457
12458 if (timeout->mTimer) {
12459 timeout->mTimer->Cancel();
12460 timeout->mTimer = nullptr;
12461
12462 // Drop the count since the timer isn't going to hold on
12463 // anymore.
12464 timeout->Release();
12465 }
12466
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;
12470
12471 // Drop the count since we're removing it from the list.
12472 timeout->Release();
12473 }
12474
12475 // Clear out our list
12476 mTimeouts.clear();
12477 }
12478
12479 void
12480 nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout)
12481 {
12482 NS_ASSERTION(IsInnerWindow(),
12483 "InsertTimeoutIntoList() called on outer window!");
12484
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 }
12499
12500 // Now link in aTimeout after prevSibling.
12501 if (prevSibling) {
12502 prevSibling->setNext(aTimeout);
12503 } else {
12504 mTimeouts.insertFront(aTimeout);
12505 }
12506
12507 aTimeout->mFiringDepth = 0;
12508
12509 // Increment the timeout's reference count since it's now held on to
12510 // by the list
12511 aTimeout->AddRef();
12512 }
12513
12514 // static
12515 void
12516 nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure)
12517 {
12518 nsRefPtr<nsTimeout> timeout = (nsTimeout *)aClosure;
12519
12520 timeout->mWindow->RunTimeout(timeout);
12521 }
12522
12523 //*****************************************************************************
12524 // nsGlobalWindow: Helper Functions
12525 //*****************************************************************************
12526
12527 already_AddRefed<nsIDocShellTreeOwner>
12528 nsGlobalWindow::GetTreeOwner()
12529 {
12530 FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
12531
12532 // If there's no docShellAsItem, this window must have been closed,
12533 // in that case there is no tree owner.
12534
12535 if (!mDocShell) {
12536 return nullptr;
12537 }
12538
12539 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
12540 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
12541 return treeOwner.forget();
12542 }
12543
12544 already_AddRefed<nsIBaseWindow>
12545 nsGlobalWindow::GetTreeOwnerWindow()
12546 {
12547 MOZ_ASSERT(IsOuterWindow());
12548
12549 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
12550
12551 // If there's no mDocShell, this window must have been closed,
12552 // in that case there is no tree owner.
12553
12554 if (mDocShell) {
12555 mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
12556 }
12557
12558 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
12559 return baseWindow.forget();
12560 }
12561
12562 already_AddRefed<nsIWebBrowserChrome>
12563 nsGlobalWindow::GetWebBrowserChrome()
12564 {
12565 nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
12566
12567 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner);
12568 return browserChrome.forget();
12569 }
12570
12571 nsIScrollableFrame *
12572 nsGlobalWindow::GetScrollFrame()
12573 {
12574 FORWARD_TO_OUTER(GetScrollFrame, (), nullptr);
12575
12576 if (!mDocShell) {
12577 return nullptr;
12578 }
12579
12580 nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
12581 if (presShell) {
12582 return presShell->GetRootScrollFrameAsScrollable();
12583 }
12584 return nullptr;
12585 }
12586
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());
12601
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 }
12619
12620 if (NS_FAILED(nsContentUtils::GetSecurityManager()->
12621 CheckLoadURIFromScript(cx, uri))) {
12622 return NS_ERROR_FAILURE;
12623 }
12624
12625 return NS_OK;
12626 }
12627
12628 void
12629 nsGlobalWindow::FlushPendingNotifications(mozFlushType aType)
12630 {
12631 if (mDoc) {
12632 mDoc->FlushPendingNotifications(aType);
12633 }
12634 }
12635
12636 void
12637 nsGlobalWindow::EnsureSizeUpToDate()
12638 {
12639 MOZ_ASSERT(IsOuterWindow());
12640
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 }
12650
12651 already_AddRefed<nsISupports>
12652 nsGlobalWindow::SaveWindowState()
12653 {
12654 NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state");
12655
12656 if (!mContext || !GetWrapperPreserveColor()) {
12657 // The window may be getting torn down; don't bother saving state.
12658 return nullptr;
12659 }
12660
12661 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
12662 NS_ASSERTION(inner, "No inner window to save");
12663
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();
12670
12671 nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
12672
12673 #ifdef DEBUG_PAGE_CACHE
12674 printf("saving window state, state = %p\n", (void*)state);
12675 #endif
12676
12677 return state.forget();
12678 }
12679
12680 nsresult
12681 nsGlobalWindow::RestoreWindowState(nsISupports *aState)
12682 {
12683 NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window");
12684
12685 if (!mContext || !GetWrapperPreserveColor()) {
12686 // The window may be getting torn down; don't bother restoring state.
12687 return NS_OK;
12688 }
12689
12690 nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
12691 NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
12692
12693 #ifdef DEBUG_PAGE_CACHE
12694 printf("restoring window state, state = %p\n", (void*)holder);
12695 #endif
12696
12697 // And we're ready to go!
12698 nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
12699
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 }
12711
12712 inner->Thaw();
12713
12714 holder->DidRestoreWindow();
12715
12716 return NS_OK;
12717 }
12718
12719 void
12720 nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
12721 bool aFreezeChildren)
12722 {
12723 FORWARD_TO_INNER_VOID(SuspendTimeouts, (aIncrease, aFreezeChildren));
12724
12725 bool suspended = (mTimeoutsSuspendDepth != 0);
12726 mTimeoutsSuspendDepth += aIncrease;
12727
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();
12735
12736 // Suspend all of the workers for this window.
12737 mozilla::dom::workers::SuspendWorkersForWindow(this);
12738
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);
12746
12747 // Drop the XPCOM timer; we'll reschedule when restoring the state.
12748 if (t->mTimer) {
12749 t->mTimer->Cancel();
12750 t->mTimer = nullptr;
12751
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 }
12759
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 }
12765
12766 // Suspend our children as well.
12767 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
12768 if (docShell) {
12769 int32_t childCount = 0;
12770 docShell->GetChildCount(&childCount);
12771
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");
12776
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();
12784
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 }
12791
12792 win->SuspendTimeouts(aIncrease, aFreezeChildren);
12793
12794 if (inner && aFreezeChildren) {
12795 inner->Freeze();
12796 }
12797 }
12798 }
12799 }
12800 }
12801
12802 nsresult
12803 nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
12804 {
12805 FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED);
12806
12807 NS_ASSERTION(mTimeoutsSuspendDepth, "Mismatched calls to ResumeTimeouts!");
12808 --mTimeoutsSuspendDepth;
12809 bool shouldResume = (mTimeoutsSuspendDepth == 0) && !mInnerObjectsFreed;
12810 nsresult rv;
12811
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();
12819
12820 // Resume all of the AudioContexts for this window
12821 for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
12822 mAudioContexts[i]->Resume();
12823 }
12824
12825 // Resume all of the workers for this window.
12826 mozilla::dom::workers::ResumeWorkersForWindow(this);
12827
12828 // Restore all of the timeouts, using the stored time remaining
12829 // (stored in timeout->mTimeRemaining).
12830
12831 TimeStamp now = TimeStamp::Now();
12832
12833 #ifdef DEBUG
12834 bool _seenDummyTimeout = false;
12835 #endif
12836
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 }
12848
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());
12855
12856 // Set mWhen back to the time when the timer is supposed to
12857 // fire.
12858 t->mWhen = now + t->mTimeRemaining;
12859
12860 t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
12861 NS_ENSURE_TRUE(t->mTimer, NS_ERROR_OUT_OF_MEMORY);
12862
12863 rv = t->InitTimer(TimerCallback, delay);
12864 if (NS_FAILED(rv)) {
12865 t->mTimer = nullptr;
12866 return rv;
12867 }
12868
12869 // Add a reference for the new timer's closure.
12870 t->AddRef();
12871 }
12872 }
12873
12874 // Resume our children as well.
12875 nsCOMPtr<nsIDocShell> docShell = GetDocShell();
12876 if (docShell) {
12877 int32_t childCount = 0;
12878 docShell->GetChildCount(&childCount);
12879
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");
12884
12885 nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell);
12886 if (pWin) {
12887 nsGlobalWindow *win =
12888 static_cast<nsGlobalWindow*>
12889 (static_cast<nsPIDOMWindow*>(pWin));
12890
12891 NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
12892 nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
12893
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 }
12900
12901 if (inner && aThawChildren) {
12902 inner->Thaw();
12903 }
12904
12905 rv = win->ResumeTimeouts(aThawChildren);
12906 NS_ENSURE_SUCCESS(rv, rv);
12907 }
12908 }
12909 }
12910
12911 return NS_OK;
12912 }
12913
12914 uint32_t
12915 nsGlobalWindow::TimeoutSuspendCount()
12916 {
12917 FORWARD_TO_INNER(TimeoutSuspendCount, (), 0);
12918 return mTimeoutsSuspendDepth;
12919 }
12920
12921 void
12922 nsGlobalWindow::EnableDeviceSensor(uint32_t aType)
12923 {
12924 MOZ_ASSERT(IsInnerWindow());
12925
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 }
12933
12934 mEnabledSensors.AppendElement(aType);
12935
12936 if (alreadyEnabled) {
12937 return;
12938 }
12939
12940 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12941 if (ac) {
12942 ac->AddWindowListener(aType, this);
12943 }
12944 }
12945
12946 void
12947 nsGlobalWindow::DisableDeviceSensor(uint32_t aType)
12948 {
12949 MOZ_ASSERT(IsInnerWindow());
12950
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 }
12959
12960 if (doomedElement == -1) {
12961 return;
12962 }
12963
12964 mEnabledSensors.RemoveElementAt(doomedElement);
12965
12966 if (listenerCount > 1) {
12967 return;
12968 }
12969
12970 nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
12971 if (ac) {
12972 ac->RemoveWindowListener(aType, this);
12973 }
12974 }
12975
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 }
12985
12986 void
12987 nsGlobalWindow::EnableTimeChangeNotifications()
12988 {
12989 mozilla::time::AddWindowListener(this);
12990 }
12991
12992 void
12993 nsGlobalWindow::DisableTimeChangeNotifications()
12994 {
12995 mozilla::time::RemoveWindowListener(this);
12996 }
12997
12998 static PLDHashOperator
12999 CollectSizeAndListenerCount(
13000 nsPtrHashKey<DOMEventTargetHelper>* aEntry,
13001 void *arg)
13002 {
13003 nsWindowSizes* windowSizes = static_cast<nsWindowSizes*>(arg);
13004
13005 DOMEventTargetHelper* et = aEntry->GetKey();
13006
13007 if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
13008 windowSizes->mDOMEventTargetsSize +=
13009 iSizeOf->SizeOfEventTargetIncludingThis(windowSizes->mMallocSizeOf);
13010 }
13011
13012 if (EventListenerManager* elm = et->GetExistingListenerManager()) {
13013 windowSizes->mDOMEventListenersCount += elm->ListenerCount();
13014 }
13015
13016 return PL_DHASH_NEXT;
13017 }
13018
13019 void
13020 nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
13021 {
13022 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
13023
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 }
13036
13037 if (mNavigator) {
13038 aWindowSizes->mDOMOtherSize +=
13039 mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
13040 }
13041
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 }
13052
13053
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 }
13061
13062 void
13063 nsGlobalWindow::RemoveGamepad(uint32_t aIndex)
13064 {
13065 FORWARD_TO_INNER_VOID(RemoveGamepad, (aIndex));
13066 mGamepads.Remove(aIndex);
13067 }
13068
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 }
13080
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 }
13090
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 }
13099
13100 return nullptr;
13101 }
13102
13103 void
13104 nsGlobalWindow::SetHasSeenGamepadInput(bool aHasSeen)
13105 {
13106 FORWARD_TO_INNER_VOID(SetHasSeenGamepadInput, (aHasSeen));
13107 mHasSeenGamepadInput = aHasSeen;
13108 }
13109
13110 bool
13111 nsGlobalWindow::HasSeenGamepadInput()
13112 {
13113 FORWARD_TO_INNER(HasSeenGamepadInput, (), false);
13114 return mHasSeenGamepadInput;
13115 }
13116
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 }
13126
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
13137
13138 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
13139
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
13145
13146
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
13156
13157 DOMCI_DATA(ChromeWindow, nsGlobalChromeWindow)
13158
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)
13164
13165 NS_IMPL_ADDREF_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
13166 NS_IMPL_RELEASE_INHERITED(nsGlobalChromeWindow, nsGlobalWindow)
13167
13168 NS_IMETHODIMP
13169 nsGlobalChromeWindow::GetWindowState(uint16_t* aWindowState)
13170 {
13171 *aWindowState = WindowState();
13172 return NS_OK;
13173 }
13174
13175 uint16_t
13176 nsGlobalWindow::WindowState()
13177 {
13178 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13179
13180 int32_t mode = widget ? widget->SizeMode() : 0;
13181
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 }
13195
13196 return nsIDOMChromeWindow::STATE_NORMAL;
13197 }
13198
13199 NS_IMETHODIMP
13200 nsGlobalChromeWindow::Maximize()
13201 {
13202 ErrorResult rv;
13203 Maximize(rv);
13204 return rv.ErrorCode();
13205 }
13206
13207 void
13208 nsGlobalWindow::Maximize(ErrorResult& aError)
13209 {
13210 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13211
13212 if (widget) {
13213 aError = widget->SetSizeMode(nsSizeMode_Maximized);
13214 }
13215 }
13216
13217 NS_IMETHODIMP
13218 nsGlobalChromeWindow::Minimize()
13219 {
13220 ErrorResult rv;
13221 Minimize(rv);
13222 return rv.ErrorCode();
13223 }
13224
13225 void
13226 nsGlobalWindow::Minimize(ErrorResult& aError)
13227 {
13228 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13229
13230 if (widget) {
13231 aError = widget->SetSizeMode(nsSizeMode_Minimized);
13232 }
13233 }
13234
13235 NS_IMETHODIMP
13236 nsGlobalChromeWindow::Restore()
13237 {
13238 ErrorResult rv;
13239 Restore(rv);
13240 return rv.ErrorCode();
13241 }
13242
13243 void
13244 nsGlobalWindow::Restore(ErrorResult& aError)
13245 {
13246 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13247
13248 if (widget) {
13249 aError = widget->SetSizeMode(nsSizeMode_Normal);
13250 }
13251 }
13252
13253 NS_IMETHODIMP
13254 nsGlobalChromeWindow::GetAttention()
13255 {
13256 ErrorResult rv;
13257 GetAttention(rv);
13258 return rv.ErrorCode();
13259 }
13260
13261 void
13262 nsGlobalWindow::GetAttention(ErrorResult& aResult)
13263 {
13264 return GetAttentionWithCycleCount(-1, aResult);
13265 }
13266
13267 NS_IMETHODIMP
13268 nsGlobalChromeWindow::GetAttentionWithCycleCount(int32_t aCycleCount)
13269 {
13270 ErrorResult rv;
13271 GetAttentionWithCycleCount(aCycleCount, rv);
13272 return rv.ErrorCode();
13273 }
13274
13275 void
13276 nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount,
13277 ErrorResult& aError)
13278 {
13279 nsCOMPtr<nsIWidget> widget = GetMainWidget();
13280
13281 if (widget) {
13282 aError = widget->GetAttention(aCycleCount);
13283 }
13284 }
13285
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);
13292
13293 nsCOMPtr<Element> panel = do_QueryInterface(aPanel);
13294 NS_ENSURE_TRUE(panel || !aPanel, NS_ERROR_FAILURE);
13295
13296 ErrorResult rv;
13297 BeginWindowMove(*mouseDownEvent, panel, rv);
13298 return rv.ErrorCode();
13299 }
13300
13301 void
13302 nsGlobalWindow::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel,
13303 ErrorResult& aError)
13304 {
13305 nsCOMPtr<nsIWidget> widget;
13306
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 }
13314
13315 widget = (static_cast<nsMenuPopupFrame*>(frame))->GetWidget();
13316 }
13317 else {
13318 #endif
13319 widget = GetMainWidget();
13320 #ifdef MOZ_XUL
13321 }
13322 #endif
13323
13324 if (!widget) {
13325 return;
13326 }
13327
13328 WidgetMouseEvent* mouseEvent =
13329 aMouseDownEvent.GetInternalNSEvent()->AsMouseEvent();
13330 if (!mouseEvent || mouseEvent->eventStructType != NS_MOUSE_EVENT) {
13331 aError.Throw(NS_ERROR_FAILURE);
13332 return;
13333 }
13334
13335 aError = widget->BeginMoveDrag(mouseEvent);
13336 }
13337
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 }
13347
13348 void
13349 nsGlobalWindow::SetCursor(const nsAString& aCursor, ErrorResult& aError)
13350 {
13351 FORWARD_TO_OUTER_OR_THROW(SetCursor, (aCursor, aError), aError, );
13352
13353 int32_t cursor;
13354
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 }
13364
13365 nsRefPtr<nsPresContext> presContext;
13366 if (mDocShell) {
13367 mDocShell->GetPresContext(getter_AddRefs(presContext));
13368 }
13369
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 }
13377
13378 nsViewManager* vm = presShell->GetViewManager();
13379 if (!vm) {
13380 aError.Throw(NS_ERROR_FAILURE);
13381 return;
13382 }
13383
13384 nsView* rootView = vm->GetRootView();
13385 if (!rootView) {
13386 aError.Throw(NS_ERROR_FAILURE);
13387 return;
13388 }
13389
13390 nsIWidget* widget = rootView->GetNearestWidget(nullptr);
13391 if (!widget) {
13392 aError.Throw(NS_ERROR_FAILURE);
13393 return;
13394 }
13395
13396 // Call esm and set cursor.
13397 aError = presContext->EventStateManager()->SetCursor(cursor, nullptr,
13398 false, 0.0f, 0.0f,
13399 widget, true);
13400 }
13401 }
13402
13403 NS_IMETHODIMP
13404 nsGlobalChromeWindow::GetBrowserDOMWindow(nsIBrowserDOMWindow **aBrowserWindow)
13405 {
13406 ErrorResult rv;
13407 NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv));
13408 return rv.ErrorCode();
13409 }
13410
13411 nsIBrowserDOMWindow*
13412 nsGlobalWindow::GetBrowserDOMWindow(ErrorResult& aError)
13413 {
13414 FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (aError), aError, nullptr);
13415
13416 MOZ_ASSERT(IsChromeWindow());
13417 return static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow;
13418 }
13419
13420 NS_IMETHODIMP
13421 nsGlobalChromeWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow *aBrowserWindow)
13422 {
13423 ErrorResult rv;
13424 SetBrowserDOMWindow(aBrowserWindow, rv);
13425 return rv.ErrorCode();
13426 }
13427
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 }
13437
13438 NS_IMETHODIMP
13439 nsGlobalChromeWindow::NotifyDefaultButtonLoaded(nsIDOMElement* aDefaultButton)
13440 {
13441 nsCOMPtr<Element> defaultButton = do_QueryInterface(aDefaultButton);
13442 NS_ENSURE_ARG(defaultButton);
13443
13444 ErrorResult rv;
13445 NotifyDefaultButtonLoaded(*defaultButton, rv);
13446 return rv.ErrorCode();
13447 }
13448
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 }
13466
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();
13474
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 }
13486
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 }
13497
13498 NS_IMETHODIMP
13499 nsGlobalChromeWindow::GetMessageManager(nsIMessageBroadcaster** aManager)
13500 {
13501 ErrorResult rv;
13502 NS_IF_ADDREF(*aManager = GetMessageManager(rv));
13503 return rv.ErrorCode();
13504 }
13505
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 }
13518
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 }
13528
13529 // nsGlobalModalWindow implementation
13530
13531 // QueryInterface implementation for nsGlobalModalWindow
13532 DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow)
13533
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)
13538
13539 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
13540 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
13541
13542
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, );
13550
13551 MOZ_ASSERT(IsModalContentWindow(),
13552 "This should only be called on modal windows!");
13553
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 }
13561
13562 NS_IMETHODIMP
13563 nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
13564 {
13565 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
13566 NS_ERROR_NOT_INITIALIZED);
13567
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 }
13572
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, );
13580
13581 MOZ_ASSERT(IsModalContentWindow(),
13582 "This should only be called on modal windows!");
13583
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 }
13593
13594 NS_IMETHODIMP
13595 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
13596 {
13597 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
13598
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 }
13607
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, );
13615
13616 MOZ_ASSERT(IsModalContentWindow(),
13617 "This should only be called on modal windows!");
13618
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 }
13628
13629 NS_IMETHODIMP
13630 nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal)
13631 {
13632 FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK);
13633
13634 mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(),
13635 aRetVal);
13636 return NS_OK;
13637 }
13638
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 }
13646
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 }
13656
13657 if (!WrapNewBindingObject(aCx, console, aConsole)) {
13658 return NS_ERROR_FAILURE;
13659 }
13660
13661 return NS_OK;
13662 }
13663
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 }
13671
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 }
13678
13679 return NS_OK;
13680 }
13681
13682 Console*
13683 nsGlobalWindow::GetConsole(ErrorResult& aRv)
13684 {
13685 FORWARD_TO_INNER_OR_THROW(GetConsole, (aRv), aRv, nullptr);
13686
13687 if (!mConsole) {
13688 mConsole = new Console(this);
13689 }
13690
13691 return mConsole;
13692 }
13693
13694 already_AddRefed<External>
13695 nsGlobalWindow::GetExternal(ErrorResult& aRv)
13696 {
13697 FORWARD_TO_INNER_OR_THROW(GetExternal, (aRv), aRv, nullptr);
13698
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 }
13710
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 }
13718
13719 void
13720 nsGlobalWindow::GetSidebar(OwningExternalOrWindowProxy& aResult,
13721 ErrorResult& aRv)
13722 {
13723 FORWARD_TO_INNER_OR_THROW(GetSidebar, (aResult, aRv), aRv, );
13724
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 }
13732
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 }
13741
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)));
13749
13750 return IsDOMObject(aObj);
13751 }
13752
13753 #ifdef MOZ_B2G
13754 void
13755 nsGlobalWindow::EnableNetworkEvent(uint32_t aType)
13756 {
13757 MOZ_ASSERT(IsInnerWindow());
13758
13759 nsCOMPtr<nsIPermissionManager> permMgr =
13760 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
13761 if (!permMgr) {
13762 NS_ERROR("No PermissionManager available!");
13763 return;
13764 }
13765
13766 uint32_t permission = nsIPermissionManager::DENY_ACTION;
13767 permMgr->TestExactPermissionFromPrincipal(GetPrincipal(), "network-events",
13768 &permission);
13769
13770 if (permission != nsIPermissionManager::ALLOW_ACTION) {
13771 return;
13772 }
13773
13774 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
13775 if (!os) {
13776 NS_ERROR("ObserverService should be available!");
13777 return;
13778 }
13779
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 }
13795
13796 void
13797 nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
13798 {
13799 MOZ_ASSERT(IsInnerWindow());
13800
13801 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
13802 if (!os) {
13803 return;
13804 }
13805
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
13822
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
13910
13911 #ifdef _WINDOWS_
13912 #error "Never include windows.h in this file!"
13913 #endif

mercurial