Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "nsGlobalWindow.h" |
michael@0 | 8 | |
michael@0 | 9 | #include <algorithm> |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/MemoryReporting.h" |
michael@0 | 12 | |
michael@0 | 13 | // Local Includes |
michael@0 | 14 | #include "Navigator.h" |
michael@0 | 15 | #include "nsScreen.h" |
michael@0 | 16 | #include "nsHistory.h" |
michael@0 | 17 | #include "nsPerformance.h" |
michael@0 | 18 | #include "nsDOMNavigationTiming.h" |
michael@0 | 19 | #include "nsIDOMStorage.h" |
michael@0 | 20 | #include "nsIDOMStorageManager.h" |
michael@0 | 21 | #include "DOMStorage.h" |
michael@0 | 22 | #include "nsDOMOfflineResourceList.h" |
michael@0 | 23 | #include "nsError.h" |
michael@0 | 24 | #include "nsIIdleService.h" |
michael@0 | 25 | #include "nsISizeOfEventTarget.h" |
michael@0 | 26 | #include "nsDOMJSUtils.h" |
michael@0 | 27 | #include "nsArrayUtils.h" |
michael@0 | 28 | #include "nsIDOMWindowCollection.h" |
michael@0 | 29 | #include "nsDOMWindowList.h" |
michael@0 | 30 | #include "mozilla/dom/WakeLock.h" |
michael@0 | 31 | #include "mozilla/dom/power/PowerManagerService.h" |
michael@0 | 32 | #include "nsIDocShellTreeOwner.h" |
michael@0 | 33 | #include "nsIPermissionManager.h" |
michael@0 | 34 | #include "nsIScriptContext.h" |
michael@0 | 35 | #include "nsIScriptTimeoutHandler.h" |
michael@0 | 36 | #include "nsIController.h" |
michael@0 | 37 | #include "nsScriptNameSpaceManager.h" |
michael@0 | 38 | #include "nsWindowMemoryReporter.h" |
michael@0 | 39 | |
michael@0 | 40 | // Helper Classes |
michael@0 | 41 | #include "nsJSUtils.h" |
michael@0 | 42 | #include "jsapi.h" // for JSAutoRequest |
michael@0 | 43 | #include "js/OldDebugAPI.h" // for JS_ClearWatchPointsForObject |
michael@0 | 44 | #include "jswrapper.h" |
michael@0 | 45 | #include "nsReadableUtils.h" |
michael@0 | 46 | #include "nsDOMClassInfo.h" |
michael@0 | 47 | #include "nsJSEnvironment.h" |
michael@0 | 48 | #include "ScriptSettings.h" |
michael@0 | 49 | #include "mozilla/Preferences.h" |
michael@0 | 50 | #include "mozilla/Likely.h" |
michael@0 | 51 | #include "mozilla/unused.h" |
michael@0 | 52 | |
michael@0 | 53 | // Other Classes |
michael@0 | 54 | #include "mozilla/dom/BarProps.h" |
michael@0 | 55 | #include "nsContentCID.h" |
michael@0 | 56 | #include "nsLayoutStatics.h" |
michael@0 | 57 | #include "nsCCUncollectableMarker.h" |
michael@0 | 58 | #include "mozilla/dom/workers/Workers.h" |
michael@0 | 59 | #include "mozilla/dom/MessagePortList.h" |
michael@0 | 60 | #include "nsJSPrincipals.h" |
michael@0 | 61 | #include "mozilla/Attributes.h" |
michael@0 | 62 | #include "mozilla/Debug.h" |
michael@0 | 63 | #include "mozilla/EventListenerManager.h" |
michael@0 | 64 | #include "mozilla/EventStates.h" |
michael@0 | 65 | #include "mozilla/MouseEvents.h" |
michael@0 | 66 | #include "AudioChannelService.h" |
michael@0 | 67 | #include "MessageEvent.h" |
michael@0 | 68 | |
michael@0 | 69 | // Interfaces Needed |
michael@0 | 70 | #include "nsIFrame.h" |
michael@0 | 71 | #include "nsCanvasFrame.h" |
michael@0 | 72 | #include "nsIWidget.h" |
michael@0 | 73 | #include "nsIWidgetListener.h" |
michael@0 | 74 | #include "nsIBaseWindow.h" |
michael@0 | 75 | #include "nsIDeviceSensors.h" |
michael@0 | 76 | #include "nsIContent.h" |
michael@0 | 77 | #include "nsIDocShell.h" |
michael@0 | 78 | #include "nsIDocCharset.h" |
michael@0 | 79 | #include "nsIDocument.h" |
michael@0 | 80 | #include "Crypto.h" |
michael@0 | 81 | #ifndef MOZ_DISABLE_CRYPTOLEGACY |
michael@0 | 82 | #include "nsIDOMCryptoLegacy.h" |
michael@0 | 83 | #endif |
michael@0 | 84 | #include "nsIDOMDocument.h" |
michael@0 | 85 | #include "nsIDOMElement.h" |
michael@0 | 86 | #include "nsIDOMEvent.h" |
michael@0 | 87 | #include "nsIDOMPopupBlockedEvent.h" |
michael@0 | 88 | #include "nsIDOMPopStateEvent.h" |
michael@0 | 89 | #include "nsIDOMHashChangeEvent.h" |
michael@0 | 90 | #include "nsIDOMOfflineResourceList.h" |
michael@0 | 91 | #include "nsPIDOMStorage.h" |
michael@0 | 92 | #include "nsDOMString.h" |
michael@0 | 93 | #include "nsIEmbeddingSiteWindow.h" |
michael@0 | 94 | #include "nsThreadUtils.h" |
michael@0 | 95 | #include "nsILoadContext.h" |
michael@0 | 96 | #include "nsIMarkupDocumentViewer.h" |
michael@0 | 97 | #include "nsIPresShell.h" |
michael@0 | 98 | #include "nsIScriptSecurityManager.h" |
michael@0 | 99 | #include "nsIScrollableFrame.h" |
michael@0 | 100 | #include "nsView.h" |
michael@0 | 101 | #include "nsViewManager.h" |
michael@0 | 102 | #include "nsISelectionController.h" |
michael@0 | 103 | #include "nsISelection.h" |
michael@0 | 104 | #include "nsIPrompt.h" |
michael@0 | 105 | #include "nsIPromptService.h" |
michael@0 | 106 | #include "nsIPromptFactory.h" |
michael@0 | 107 | #include "nsIWritablePropertyBag2.h" |
michael@0 | 108 | #include "nsIWebNavigation.h" |
michael@0 | 109 | #include "nsIWebBrowserChrome.h" |
michael@0 | 110 | #include "nsIWebBrowserFind.h" // For window.find() |
michael@0 | 111 | #include "nsIWindowMediator.h" // For window.find() |
michael@0 | 112 | #include "nsComputedDOMStyle.h" |
michael@0 | 113 | #include "nsIEntropyCollector.h" |
michael@0 | 114 | #include "nsDOMCID.h" |
michael@0 | 115 | #include "nsDOMWindowUtils.h" |
michael@0 | 116 | #include "nsIWindowWatcher.h" |
michael@0 | 117 | #include "nsPIWindowWatcher.h" |
michael@0 | 118 | #include "nsIContentViewer.h" |
michael@0 | 119 | #include "nsIScriptError.h" |
michael@0 | 120 | #include "nsIControllers.h" |
michael@0 | 121 | #include "nsIControllerContext.h" |
michael@0 | 122 | #include "nsGlobalWindowCommands.h" |
michael@0 | 123 | #include "nsAutoPtr.h" |
michael@0 | 124 | #include "nsContentUtils.h" |
michael@0 | 125 | #include "nsCxPusher.h" |
michael@0 | 126 | #include "nsCSSProps.h" |
michael@0 | 127 | #include "nsIDOMFile.h" |
michael@0 | 128 | #include "nsIDOMFileList.h" |
michael@0 | 129 | #include "nsIURIFixup.h" |
michael@0 | 130 | #ifndef DEBUG |
michael@0 | 131 | #include "nsIAppStartup.h" |
michael@0 | 132 | #include "nsToolkitCompsCID.h" |
michael@0 | 133 | #endif |
michael@0 | 134 | #include "nsCDefaultURIFixup.h" |
michael@0 | 135 | #include "mozilla/EventDispatcher.h" |
michael@0 | 136 | #include "mozilla/EventStateManager.h" |
michael@0 | 137 | #include "nsIObserverService.h" |
michael@0 | 138 | #include "nsFocusManager.h" |
michael@0 | 139 | #include "nsIXULWindow.h" |
michael@0 | 140 | #include "nsITimedChannel.h" |
michael@0 | 141 | #include "nsServiceManagerUtils.h" |
michael@0 | 142 | #ifdef MOZ_XUL |
michael@0 | 143 | #include "nsIDOMXULControlElement.h" |
michael@0 | 144 | #include "nsMenuPopupFrame.h" |
michael@0 | 145 | #endif |
michael@0 | 146 | #include "nsIDOMCustomEvent.h" |
michael@0 | 147 | #include "nsIFrameRequestCallback.h" |
michael@0 | 148 | #include "nsIJARChannel.h" |
michael@0 | 149 | |
michael@0 | 150 | #include "xpcprivate.h" |
michael@0 | 151 | |
michael@0 | 152 | #ifdef NS_PRINTING |
michael@0 | 153 | #include "nsIPrintSettings.h" |
michael@0 | 154 | #include "nsIPrintSettingsService.h" |
michael@0 | 155 | #include "nsIWebBrowserPrint.h" |
michael@0 | 156 | #endif |
michael@0 | 157 | |
michael@0 | 158 | #include "nsWindowRoot.h" |
michael@0 | 159 | #include "nsNetCID.h" |
michael@0 | 160 | #include "nsIArray.h" |
michael@0 | 161 | |
michael@0 | 162 | // XXX An unfortunate dependency exists here (two XUL files). |
michael@0 | 163 | #include "nsIDOMXULDocument.h" |
michael@0 | 164 | #include "nsIDOMXULCommandDispatcher.h" |
michael@0 | 165 | |
michael@0 | 166 | #include "nsBindingManager.h" |
michael@0 | 167 | #include "nsXBLService.h" |
michael@0 | 168 | |
michael@0 | 169 | // used for popup blocking, needs to be converted to something |
michael@0 | 170 | // belonging to the back-end like nsIContentPolicy |
michael@0 | 171 | #include "nsIPopupWindowManager.h" |
michael@0 | 172 | |
michael@0 | 173 | #include "nsIDragService.h" |
michael@0 | 174 | #include "mozilla/dom/Element.h" |
michael@0 | 175 | #include "mozilla/dom/Selection.h" |
michael@0 | 176 | #include "nsFrameLoader.h" |
michael@0 | 177 | #include "nsISupportsPrimitives.h" |
michael@0 | 178 | #include "nsXPCOMCID.h" |
michael@0 | 179 | #include "GeneratedEvents.h" |
michael@0 | 180 | #include "GeneratedEventClasses.h" |
michael@0 | 181 | #include "mozIThirdPartyUtil.h" |
michael@0 | 182 | #ifdef MOZ_LOGGING |
michael@0 | 183 | // so we can get logging even in release builds |
michael@0 | 184 | #define FORCE_PR_LOG 1 |
michael@0 | 185 | #endif |
michael@0 | 186 | #include "prlog.h" |
michael@0 | 187 | #include "prenv.h" |
michael@0 | 188 | #include "prprf.h" |
michael@0 | 189 | |
michael@0 | 190 | #include "mozilla/dom/MessageChannel.h" |
michael@0 | 191 | #include "mozilla/dom/MessagePort.h" |
michael@0 | 192 | #include "mozilla/dom/MessagePortBinding.h" |
michael@0 | 193 | #include "mozilla/dom/indexedDB/IDBFactory.h" |
michael@0 | 194 | #include "mozilla/dom/quota/QuotaManager.h" |
michael@0 | 195 | |
michael@0 | 196 | #include "mozilla/dom/StructuredCloneTags.h" |
michael@0 | 197 | |
michael@0 | 198 | #ifdef MOZ_GAMEPAD |
michael@0 | 199 | #include "mozilla/dom/GamepadService.h" |
michael@0 | 200 | #endif |
michael@0 | 201 | |
michael@0 | 202 | #include "nsRefreshDriver.h" |
michael@0 | 203 | |
michael@0 | 204 | #include "mozilla/Services.h" |
michael@0 | 205 | #include "mozilla/Telemetry.h" |
michael@0 | 206 | #include "nsLocation.h" |
michael@0 | 207 | #include "nsHTMLDocument.h" |
michael@0 | 208 | #include "nsWrapperCacheInlines.h" |
michael@0 | 209 | #include "mozilla/DOMEventTargetHelper.h" |
michael@0 | 210 | #include "prrng.h" |
michael@0 | 211 | #include "nsSandboxFlags.h" |
michael@0 | 212 | #include "TimeChangeObserver.h" |
michael@0 | 213 | #include "mozilla/dom/AudioContext.h" |
michael@0 | 214 | #include "mozilla/dom/BrowserElementDictionariesBinding.h" |
michael@0 | 215 | #include "mozilla/dom/Console.h" |
michael@0 | 216 | #include "mozilla/dom/FunctionBinding.h" |
michael@0 | 217 | #include "mozilla/dom/WindowBinding.h" |
michael@0 | 218 | #include "nsITabChild.h" |
michael@0 | 219 | #include "mozilla/dom/MediaQueryList.h" |
michael@0 | 220 | #include "mozilla/dom/ScriptSettings.h" |
michael@0 | 221 | #ifdef HAVE_SIDEBAR |
michael@0 | 222 | #include "mozilla/dom/ExternalBinding.h" |
michael@0 | 223 | #endif |
michael@0 | 224 | |
michael@0 | 225 | #ifdef MOZ_WEBSPEECH |
michael@0 | 226 | #include "mozilla/dom/SpeechSynthesis.h" |
michael@0 | 227 | #endif |
michael@0 | 228 | |
michael@0 | 229 | #ifdef MOZ_JSDEBUGGER |
michael@0 | 230 | #include "jsdIDebuggerService.h" |
michael@0 | 231 | #endif |
michael@0 | 232 | |
michael@0 | 233 | #ifdef MOZ_B2G |
michael@0 | 234 | #include "nsPISocketTransportService.h" |
michael@0 | 235 | #endif |
michael@0 | 236 | |
michael@0 | 237 | // Apple system headers seem to have a check() macro. <sigh> |
michael@0 | 238 | #ifdef check |
michael@0 | 239 | class nsIScriptTimeoutHandler; |
michael@0 | 240 | #undef check |
michael@0 | 241 | #endif // check |
michael@0 | 242 | #include "AccessCheck.h" |
michael@0 | 243 | |
michael@0 | 244 | #ifdef ANDROID |
michael@0 | 245 | #include <android/log.h> |
michael@0 | 246 | #endif |
michael@0 | 247 | |
michael@0 | 248 | #ifdef PR_LOGGING |
michael@0 | 249 | static PRLogModuleInfo* gDOMLeakPRLog; |
michael@0 | 250 | #endif |
michael@0 | 251 | |
michael@0 | 252 | #ifdef XP_WIN |
michael@0 | 253 | #include <process.h> |
michael@0 | 254 | #define getpid _getpid |
michael@0 | 255 | #else |
michael@0 | 256 | #include <unistd.h> // for getpid() |
michael@0 | 257 | #endif |
michael@0 | 258 | |
michael@0 | 259 | static const char kStorageEnabled[] = "dom.storage.enabled"; |
michael@0 | 260 | |
michael@0 | 261 | using namespace mozilla; |
michael@0 | 262 | using namespace mozilla::dom; |
michael@0 | 263 | using namespace mozilla::dom::ipc; |
michael@0 | 264 | using mozilla::TimeStamp; |
michael@0 | 265 | using mozilla::TimeDuration; |
michael@0 | 266 | |
michael@0 | 267 | nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr; |
michael@0 | 268 | bool nsGlobalWindow::sWarnedAboutWindowInternal = false; |
michael@0 | 269 | bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false; |
michael@0 | 270 | |
michael@0 | 271 | static nsIEntropyCollector *gEntropyCollector = nullptr; |
michael@0 | 272 | static int32_t gRefCnt = 0; |
michael@0 | 273 | static int32_t gOpenPopupSpamCount = 0; |
michael@0 | 274 | static PopupControlState gPopupControlState = openAbused; |
michael@0 | 275 | static int32_t gRunningTimeoutDepth = 0; |
michael@0 | 276 | static bool gMouseDown = false; |
michael@0 | 277 | static bool gDragServiceDisabled = false; |
michael@0 | 278 | static FILE *gDumpFile = nullptr; |
michael@0 | 279 | static uint64_t gNextWindowID = 0; |
michael@0 | 280 | static uint32_t gSerialCounter = 0; |
michael@0 | 281 | static uint32_t gTimeoutsRecentlySet = 0; |
michael@0 | 282 | static TimeStamp gLastRecordedRecentTimeouts; |
michael@0 | 283 | #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC) |
michael@0 | 284 | |
michael@0 | 285 | #ifdef DEBUG_jst |
michael@0 | 286 | int32_t gTimeoutCnt = 0; |
michael@0 | 287 | #endif |
michael@0 | 288 | |
michael@0 | 289 | #if defined(DEBUG_bryner) || defined(DEBUG_chb) |
michael@0 | 290 | #define DEBUG_PAGE_CACHE |
michael@0 | 291 | #endif |
michael@0 | 292 | |
michael@0 | 293 | #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added" |
michael@0 | 294 | |
michael@0 | 295 | // The default shortest interval/timeout we permit |
michael@0 | 296 | #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms |
michael@0 | 297 | #define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms |
michael@0 | 298 | static int32_t gMinTimeoutValue; |
michael@0 | 299 | static int32_t gMinBackgroundTimeoutValue; |
michael@0 | 300 | inline int32_t |
michael@0 | 301 | nsGlobalWindow::DOMMinTimeoutValue() const { |
michael@0 | 302 | bool isBackground = !mOuterWindow || mOuterWindow->IsBackground(); |
michael@0 | 303 | return |
michael@0 | 304 | std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, 0); |
michael@0 | 305 | } |
michael@0 | 306 | |
michael@0 | 307 | // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit |
michael@0 | 308 | // uses 5. |
michael@0 | 309 | #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5 |
michael@0 | 310 | |
michael@0 | 311 | // The longest interval (as PRIntervalTime) we permit, or that our |
michael@0 | 312 | // timer code can handle, really. See DELAY_INTERVAL_LIMIT in |
michael@0 | 313 | // nsTimerImpl.h for details. |
michael@0 | 314 | #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT |
michael@0 | 315 | |
michael@0 | 316 | #define FORWARD_TO_OUTER(method, args, err_rval) \ |
michael@0 | 317 | PR_BEGIN_MACRO \ |
michael@0 | 318 | if (IsInnerWindow()) { \ |
michael@0 | 319 | nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
michael@0 | 320 | if (!HasActiveDocument()) { \ |
michael@0 | 321 | NS_WARNING(outer ? \ |
michael@0 | 322 | "Inner window does not have active document." : \ |
michael@0 | 323 | "No outer window available!"); \ |
michael@0 | 324 | return err_rval; \ |
michael@0 | 325 | } \ |
michael@0 | 326 | return outer->method args; \ |
michael@0 | 327 | } \ |
michael@0 | 328 | PR_END_MACRO |
michael@0 | 329 | |
michael@0 | 330 | #define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \ |
michael@0 | 331 | PR_BEGIN_MACRO \ |
michael@0 | 332 | if (IsInnerWindow()) { \ |
michael@0 | 333 | nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
michael@0 | 334 | if (!HasActiveDocument()) { \ |
michael@0 | 335 | if (!outer) { \ |
michael@0 | 336 | NS_WARNING("No outer window available!"); \ |
michael@0 | 337 | errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ |
michael@0 | 338 | } else { \ |
michael@0 | 339 | errorresult.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \ |
michael@0 | 340 | } \ |
michael@0 | 341 | } else { \ |
michael@0 | 342 | return outer->method args; \ |
michael@0 | 343 | } \ |
michael@0 | 344 | return err_rval; \ |
michael@0 | 345 | } \ |
michael@0 | 346 | PR_END_MACRO |
michael@0 | 347 | |
michael@0 | 348 | #define FORWARD_TO_OUTER_VOID(method, args) \ |
michael@0 | 349 | PR_BEGIN_MACRO \ |
michael@0 | 350 | if (IsInnerWindow()) { \ |
michael@0 | 351 | nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
michael@0 | 352 | if (!HasActiveDocument()) { \ |
michael@0 | 353 | NS_WARNING(outer ? \ |
michael@0 | 354 | "Inner window does not have active document." : \ |
michael@0 | 355 | "No outer window available!"); \ |
michael@0 | 356 | return; \ |
michael@0 | 357 | } \ |
michael@0 | 358 | outer->method args; \ |
michael@0 | 359 | return; \ |
michael@0 | 360 | } \ |
michael@0 | 361 | PR_END_MACRO |
michael@0 | 362 | |
michael@0 | 363 | #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \ |
michael@0 | 364 | PR_BEGIN_MACRO \ |
michael@0 | 365 | if (IsInnerWindow()) { \ |
michael@0 | 366 | nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
michael@0 | 367 | if (!HasActiveDocument()) { \ |
michael@0 | 368 | NS_WARNING(outer ? \ |
michael@0 | 369 | "Inner window does not have active document." : \ |
michael@0 | 370 | "No outer window available!"); \ |
michael@0 | 371 | return err_rval; \ |
michael@0 | 372 | } \ |
michael@0 | 373 | return ((nsGlobalChromeWindow *)outer)->method args; \ |
michael@0 | 374 | } \ |
michael@0 | 375 | PR_END_MACRO |
michael@0 | 376 | |
michael@0 | 377 | #define FORWARD_TO_INNER_CHROME(method, args, err_rval) \ |
michael@0 | 378 | PR_BEGIN_MACRO \ |
michael@0 | 379 | if (IsOuterWindow()) { \ |
michael@0 | 380 | if (!mInnerWindow) { \ |
michael@0 | 381 | NS_WARNING("No inner window available!"); \ |
michael@0 | 382 | return err_rval; \ |
michael@0 | 383 | } \ |
michael@0 | 384 | return ((nsGlobalChromeWindow *)mInnerWindow)->method args; \ |
michael@0 | 385 | } \ |
michael@0 | 386 | PR_END_MACRO |
michael@0 | 387 | |
michael@0 | 388 | #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ |
michael@0 | 389 | PR_BEGIN_MACRO \ |
michael@0 | 390 | if (IsInnerWindow()) { \ |
michael@0 | 391 | nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
michael@0 | 392 | if (!HasActiveDocument()) { \ |
michael@0 | 393 | NS_WARNING(outer ? \ |
michael@0 | 394 | "Inner window does not have active document." : \ |
michael@0 | 395 | "No outer window available!"); \ |
michael@0 | 396 | return err_rval; \ |
michael@0 | 397 | } \ |
michael@0 | 398 | return ((nsGlobalModalWindow *)outer)->method args; \ |
michael@0 | 399 | } \ |
michael@0 | 400 | PR_END_MACRO |
michael@0 | 401 | |
michael@0 | 402 | #define FORWARD_TO_INNER(method, args, err_rval) \ |
michael@0 | 403 | PR_BEGIN_MACRO \ |
michael@0 | 404 | if (IsOuterWindow()) { \ |
michael@0 | 405 | if (!mInnerWindow) { \ |
michael@0 | 406 | NS_WARNING("No inner window available!"); \ |
michael@0 | 407 | return err_rval; \ |
michael@0 | 408 | } \ |
michael@0 | 409 | return GetCurrentInnerWindowInternal()->method args; \ |
michael@0 | 410 | } \ |
michael@0 | 411 | PR_END_MACRO |
michael@0 | 412 | |
michael@0 | 413 | #define FORWARD_TO_INNER_OR_THROW(method, args, errorresult, err_rval) \ |
michael@0 | 414 | PR_BEGIN_MACRO \ |
michael@0 | 415 | if (IsOuterWindow()) { \ |
michael@0 | 416 | if (!mInnerWindow) { \ |
michael@0 | 417 | NS_WARNING("No inner window available!"); \ |
michael@0 | 418 | errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ |
michael@0 | 419 | return err_rval; \ |
michael@0 | 420 | } \ |
michael@0 | 421 | return GetCurrentInnerWindowInternal()->method args; \ |
michael@0 | 422 | } \ |
michael@0 | 423 | PR_END_MACRO |
michael@0 | 424 | |
michael@0 | 425 | #define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ |
michael@0 | 426 | PR_BEGIN_MACRO \ |
michael@0 | 427 | if (IsOuterWindow()) { \ |
michael@0 | 428 | if (!mInnerWindow) { \ |
michael@0 | 429 | NS_WARNING("No inner window available!"); \ |
michael@0 | 430 | return err_rval; \ |
michael@0 | 431 | } \ |
michael@0 | 432 | return ((nsGlobalModalWindow*)GetCurrentInnerWindowInternal())->method args; \ |
michael@0 | 433 | } \ |
michael@0 | 434 | PR_END_MACRO |
michael@0 | 435 | |
michael@0 | 436 | #define FORWARD_TO_INNER_VOID(method, args) \ |
michael@0 | 437 | PR_BEGIN_MACRO \ |
michael@0 | 438 | if (IsOuterWindow()) { \ |
michael@0 | 439 | if (!mInnerWindow) { \ |
michael@0 | 440 | NS_WARNING("No inner window available!"); \ |
michael@0 | 441 | return; \ |
michael@0 | 442 | } \ |
michael@0 | 443 | GetCurrentInnerWindowInternal()->method args; \ |
michael@0 | 444 | return; \ |
michael@0 | 445 | } \ |
michael@0 | 446 | PR_END_MACRO |
michael@0 | 447 | |
michael@0 | 448 | // Same as FORWARD_TO_INNER, but this will create a fresh inner if an |
michael@0 | 449 | // inner doesn't already exists. |
michael@0 | 450 | #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \ |
michael@0 | 451 | PR_BEGIN_MACRO \ |
michael@0 | 452 | if (IsOuterWindow()) { \ |
michael@0 | 453 | if (!mInnerWindow) { \ |
michael@0 | 454 | if (mIsClosed) { \ |
michael@0 | 455 | return err_rval; \ |
michael@0 | 456 | } \ |
michael@0 | 457 | nsCOMPtr<nsIDOMDocument> doc; \ |
michael@0 | 458 | nsresult fwdic_nr = GetDocument(getter_AddRefs(doc)); \ |
michael@0 | 459 | NS_ENSURE_SUCCESS(fwdic_nr, err_rval); \ |
michael@0 | 460 | if (!mInnerWindow) { \ |
michael@0 | 461 | return err_rval; \ |
michael@0 | 462 | } \ |
michael@0 | 463 | } \ |
michael@0 | 464 | return GetCurrentInnerWindowInternal()->method args; \ |
michael@0 | 465 | } \ |
michael@0 | 466 | PR_END_MACRO |
michael@0 | 467 | |
michael@0 | 468 | // CIDs |
michael@0 | 469 | static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); |
michael@0 | 470 | |
michael@0 | 471 | static const char sPopStatePrefStr[] = "browser.history.allowPopState"; |
michael@0 | 472 | |
michael@0 | 473 | #define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload") |
michael@0 | 474 | #define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload") |
michael@0 | 475 | |
michael@0 | 476 | /** |
michael@0 | 477 | * An indirect observer object that means we don't have to implement nsIObserver |
michael@0 | 478 | * on nsGlobalWindow, where any script could see it. |
michael@0 | 479 | */ |
michael@0 | 480 | class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver, |
michael@0 | 481 | public nsIInterfaceRequestor |
michael@0 | 482 | { |
michael@0 | 483 | public: |
michael@0 | 484 | nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {} |
michael@0 | 485 | NS_DECL_ISUPPORTS |
michael@0 | 486 | NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) |
michael@0 | 487 | { |
michael@0 | 488 | if (!mWindow) |
michael@0 | 489 | return NS_OK; |
michael@0 | 490 | return mWindow->Observe(aSubject, aTopic, aData); |
michael@0 | 491 | } |
michael@0 | 492 | void Forget() { mWindow = nullptr; } |
michael@0 | 493 | NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult) |
michael@0 | 494 | { |
michael@0 | 495 | if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { |
michael@0 | 496 | return mWindow->QueryInterface(aIID, aResult); |
michael@0 | 497 | } |
michael@0 | 498 | return NS_NOINTERFACE; |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | private: |
michael@0 | 502 | nsGlobalWindow* mWindow; |
michael@0 | 503 | }; |
michael@0 | 504 | |
michael@0 | 505 | NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) |
michael@0 | 506 | |
michael@0 | 507 | nsTimeout::nsTimeout() |
michael@0 | 508 | : mCleared(false), |
michael@0 | 509 | mRunning(false), |
michael@0 | 510 | mIsInterval(false), |
michael@0 | 511 | mPublicId(0), |
michael@0 | 512 | mInterval(0), |
michael@0 | 513 | mFiringDepth(0), |
michael@0 | 514 | mNestingLevel(0), |
michael@0 | 515 | mPopupState(openAllowed) |
michael@0 | 516 | { |
michael@0 | 517 | #ifdef DEBUG_jst |
michael@0 | 518 | { |
michael@0 | 519 | extern int gTimeoutCnt; |
michael@0 | 520 | |
michael@0 | 521 | ++gTimeoutCnt; |
michael@0 | 522 | } |
michael@0 | 523 | #endif |
michael@0 | 524 | |
michael@0 | 525 | MOZ_COUNT_CTOR(nsTimeout); |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | nsTimeout::~nsTimeout() |
michael@0 | 529 | { |
michael@0 | 530 | #ifdef DEBUG_jst |
michael@0 | 531 | { |
michael@0 | 532 | extern int gTimeoutCnt; |
michael@0 | 533 | |
michael@0 | 534 | --gTimeoutCnt; |
michael@0 | 535 | } |
michael@0 | 536 | #endif |
michael@0 | 537 | |
michael@0 | 538 | if (mTimer) { |
michael@0 | 539 | mTimer->Cancel(); |
michael@0 | 540 | mTimer = nullptr; |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | MOZ_COUNT_DTOR(nsTimeout); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout) |
michael@0 | 547 | |
michael@0 | 548 | NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout) |
michael@0 | 549 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout) |
michael@0 | 550 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) |
michael@0 | 551 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) |
michael@0 | 552 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler) |
michael@0 | 553 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 554 | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef) |
michael@0 | 555 | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release) |
michael@0 | 556 | |
michael@0 | 557 | // Return true if this timeout has a refcount of 1. This is used to check |
michael@0 | 558 | // that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout. |
michael@0 | 559 | bool |
michael@0 | 560 | nsTimeout::HasRefCntOne() |
michael@0 | 561 | { |
michael@0 | 562 | return mRefCnt.get() == 1; |
michael@0 | 563 | } |
michael@0 | 564 | |
michael@0 | 565 | nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow) |
michael@0 | 566 | : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0), |
michael@0 | 567 | mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false), |
michael@0 | 568 | mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr), |
michael@0 | 569 | mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false), |
michael@0 | 570 | mMayHaveMouseEnterLeaveEventListener(false), |
michael@0 | 571 | mMayHavePointerEnterLeaveEventListener(false), |
michael@0 | 572 | mIsModalContentWindow(false), |
michael@0 | 573 | mIsActive(false), mIsBackground(false), |
michael@0 | 574 | mAudioMuted(false), mAudioVolume(1.0), |
michael@0 | 575 | mInnerWindow(nullptr), mOuterWindow(aOuterWindow), |
michael@0 | 576 | // Make sure no actual window ends up with mWindowID == 0 |
michael@0 | 577 | mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false), |
michael@0 | 578 | mMarkedCCGeneration(0) |
michael@0 | 579 | {} |
michael@0 | 580 | |
michael@0 | 581 | nsPIDOMWindow::~nsPIDOMWindow() {} |
michael@0 | 582 | |
michael@0 | 583 | // DialogValueHolder CC goop. |
michael@0 | 584 | NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue) |
michael@0 | 585 | |
michael@0 | 586 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder) |
michael@0 | 587 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
michael@0 | 588 | NS_INTERFACE_MAP_END |
michael@0 | 589 | |
michael@0 | 590 | NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder) |
michael@0 | 591 | NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder) |
michael@0 | 592 | |
michael@0 | 593 | //***************************************************************************** |
michael@0 | 594 | // nsOuterWindowProxy: Outer Window Proxy |
michael@0 | 595 | //***************************************************************************** |
michael@0 | 596 | |
michael@0 | 597 | class nsOuterWindowProxy : public js::Wrapper |
michael@0 | 598 | { |
michael@0 | 599 | public: |
michael@0 | 600 | nsOuterWindowProxy() : js::Wrapper(0) { } |
michael@0 | 601 | |
michael@0 | 602 | virtual bool finalizeInBackground(JS::Value priv) { |
michael@0 | 603 | return false; |
michael@0 | 604 | } |
michael@0 | 605 | |
michael@0 | 606 | virtual const char *className(JSContext *cx, |
michael@0 | 607 | JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE; |
michael@0 | 608 | virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE; |
michael@0 | 609 | |
michael@0 | 610 | // Fundamental traps |
michael@0 | 611 | virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) |
michael@0 | 612 | MOZ_OVERRIDE; |
michael@0 | 613 | virtual bool preventExtensions(JSContext *cx, |
michael@0 | 614 | JS::Handle<JSObject*> proxy) MOZ_OVERRIDE; |
michael@0 | 615 | virtual bool getPropertyDescriptor(JSContext* cx, |
michael@0 | 616 | JS::Handle<JSObject*> proxy, |
michael@0 | 617 | JS::Handle<jsid> id, |
michael@0 | 618 | JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
michael@0 | 619 | virtual bool getOwnPropertyDescriptor(JSContext* cx, |
michael@0 | 620 | JS::Handle<JSObject*> proxy, |
michael@0 | 621 | JS::Handle<jsid> id, |
michael@0 | 622 | JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
michael@0 | 623 | virtual bool defineProperty(JSContext* cx, |
michael@0 | 624 | JS::Handle<JSObject*> proxy, |
michael@0 | 625 | JS::Handle<jsid> id, |
michael@0 | 626 | JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
michael@0 | 627 | virtual bool getOwnPropertyNames(JSContext *cx, |
michael@0 | 628 | JS::Handle<JSObject*> proxy, |
michael@0 | 629 | JS::AutoIdVector &props) MOZ_OVERRIDE; |
michael@0 | 630 | virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 631 | JS::Handle<jsid> id, |
michael@0 | 632 | bool *bp) MOZ_OVERRIDE; |
michael@0 | 633 | virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 634 | JS::AutoIdVector &props) MOZ_OVERRIDE; |
michael@0 | 635 | |
michael@0 | 636 | virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 637 | JS::Handle<jsid> id, JS::Handle<JSObject*> callable) MOZ_OVERRIDE; |
michael@0 | 638 | virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 639 | JS::Handle<jsid> id) MOZ_OVERRIDE; |
michael@0 | 640 | |
michael@0 | 641 | // Derived traps |
michael@0 | 642 | virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 643 | JS::Handle<jsid> id, bool *bp) MOZ_OVERRIDE; |
michael@0 | 644 | virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 645 | JS::Handle<jsid> id, bool *bp) MOZ_OVERRIDE; |
michael@0 | 646 | virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 647 | JS::Handle<JSObject*> receiver, |
michael@0 | 648 | JS::Handle<jsid> id, |
michael@0 | 649 | JS::MutableHandle<JS::Value> vp) MOZ_OVERRIDE; |
michael@0 | 650 | virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 651 | JS::Handle<JSObject*> receiver, |
michael@0 | 652 | JS::Handle<jsid> id, |
michael@0 | 653 | bool strict, |
michael@0 | 654 | JS::MutableHandle<JS::Value> vp) MOZ_OVERRIDE; |
michael@0 | 655 | virtual bool keys(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 656 | JS::AutoIdVector &props) MOZ_OVERRIDE; |
michael@0 | 657 | virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 658 | unsigned flags, |
michael@0 | 659 | JS::MutableHandle<JS::Value> vp) MOZ_OVERRIDE; |
michael@0 | 660 | |
michael@0 | 661 | static nsOuterWindowProxy singleton; |
michael@0 | 662 | |
michael@0 | 663 | protected: |
michael@0 | 664 | nsGlobalWindow* GetWindow(JSObject *proxy) |
michael@0 | 665 | { |
michael@0 | 666 | return nsGlobalWindow::FromSupports( |
michael@0 | 667 | static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate())); |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | // False return value means we threw an exception. True return value |
michael@0 | 671 | // but false "found" means we didn't have a subframe at that index. |
michael@0 | 672 | bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 673 | JS::Handle<jsid> id, |
michael@0 | 674 | JS::MutableHandle<JS::Value> vp, |
michael@0 | 675 | bool &found); |
michael@0 | 676 | |
michael@0 | 677 | // Returns a non-null window only if id is an index and we have a |
michael@0 | 678 | // window at that index. |
michael@0 | 679 | already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx, |
michael@0 | 680 | JS::Handle<JSObject*> proxy, |
michael@0 | 681 | JS::Handle<jsid> id); |
michael@0 | 682 | |
michael@0 | 683 | bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, |
michael@0 | 684 | JS::AutoIdVector &props); |
michael@0 | 685 | }; |
michael@0 | 686 | |
michael@0 | 687 | const js::Class OuterWindowProxyClass = |
michael@0 | 688 | PROXY_CLASS_WITH_EXT( |
michael@0 | 689 | "Proxy", |
michael@0 | 690 | 0, /* additional slots */ |
michael@0 | 691 | 0, /* additional class flags */ |
michael@0 | 692 | nullptr, /* call */ |
michael@0 | 693 | nullptr, /* construct */ |
michael@0 | 694 | PROXY_MAKE_EXT( |
michael@0 | 695 | nullptr, /* outerObject */ |
michael@0 | 696 | js::proxy_innerObject, |
michael@0 | 697 | nullptr, /* iteratorObject */ |
michael@0 | 698 | false /* isWrappedNative */ |
michael@0 | 699 | )); |
michael@0 | 700 | |
michael@0 | 701 | bool |
michael@0 | 702 | nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 703 | bool *extensible) |
michael@0 | 704 | { |
michael@0 | 705 | // If [[Extensible]] could be false, then navigating a window could navigate |
michael@0 | 706 | // to a window that's [[Extensible]] after being at one that wasn't: an |
michael@0 | 707 | // invariant violation. So always report true for this. |
michael@0 | 708 | *extensible = true; |
michael@0 | 709 | return true; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | bool |
michael@0 | 713 | nsOuterWindowProxy::preventExtensions(JSContext *cx, |
michael@0 | 714 | JS::Handle<JSObject*> proxy) |
michael@0 | 715 | { |
michael@0 | 716 | // See above. |
michael@0 | 717 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 718 | JSMSG_CANT_CHANGE_EXTENSIBILITY); |
michael@0 | 719 | return false; |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | const char * |
michael@0 | 723 | nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) |
michael@0 | 724 | { |
michael@0 | 725 | MOZ_ASSERT(js::IsProxy(proxy)); |
michael@0 | 726 | |
michael@0 | 727 | return "Window"; |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | void |
michael@0 | 731 | nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) |
michael@0 | 732 | { |
michael@0 | 733 | nsGlobalWindow* global = GetWindow(proxy); |
michael@0 | 734 | if (global) { |
michael@0 | 735 | global->ClearWrapper(); |
michael@0 | 736 | |
michael@0 | 737 | // Ideally we would use OnFinalize here, but it's possible that |
michael@0 | 738 | // EnsureScriptEnvironment will later be called on the window, and we don't |
michael@0 | 739 | // want to create a new script object in that case. Therefore, we need to |
michael@0 | 740 | // write a non-null value that will reliably crash when dereferenced. |
michael@0 | 741 | global->PoisonOuterWindowProxy(proxy); |
michael@0 | 742 | } |
michael@0 | 743 | } |
michael@0 | 744 | |
michael@0 | 745 | bool |
michael@0 | 746 | nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx, |
michael@0 | 747 | JS::Handle<JSObject*> proxy, |
michael@0 | 748 | JS::Handle<jsid> id, |
michael@0 | 749 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 750 | { |
michael@0 | 751 | // The only thing we can do differently from js::Wrapper is shadow stuff with |
michael@0 | 752 | // our indexed properties, so we can just try getOwnPropertyDescriptor and if |
michael@0 | 753 | // that gives us nothing call on through to js::Wrapper. |
michael@0 | 754 | desc.object().set(nullptr); |
michael@0 | 755 | if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) { |
michael@0 | 756 | return false; |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | if (desc.object()) { |
michael@0 | 760 | return true; |
michael@0 | 761 | } |
michael@0 | 762 | |
michael@0 | 763 | return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc); |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | bool |
michael@0 | 767 | nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx, |
michael@0 | 768 | JS::Handle<JSObject*> proxy, |
michael@0 | 769 | JS::Handle<jsid> id, |
michael@0 | 770 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 771 | { |
michael@0 | 772 | bool found; |
michael@0 | 773 | if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) { |
michael@0 | 774 | return false; |
michael@0 | 775 | } |
michael@0 | 776 | if (found) { |
michael@0 | 777 | FillPropertyDescriptor(desc, proxy, true); |
michael@0 | 778 | return true; |
michael@0 | 779 | } |
michael@0 | 780 | // else fall through to js::Wrapper |
michael@0 | 781 | |
michael@0 | 782 | return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc); |
michael@0 | 783 | } |
michael@0 | 784 | |
michael@0 | 785 | bool |
michael@0 | 786 | nsOuterWindowProxy::defineProperty(JSContext* cx, |
michael@0 | 787 | JS::Handle<JSObject*> proxy, |
michael@0 | 788 | JS::Handle<jsid> id, |
michael@0 | 789 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 790 | { |
michael@0 | 791 | int32_t index = GetArrayIndexFromId(cx, id); |
michael@0 | 792 | if (IsArrayIndex(index)) { |
michael@0 | 793 | // Spec says to Reject whether this is a supported index or not, |
michael@0 | 794 | // since we have no indexed setter or indexed creator. That means |
michael@0 | 795 | // throwing in strict mode (FIXME: Bug 828137), doing nothing in |
michael@0 | 796 | // non-strict mode. |
michael@0 | 797 | return true; |
michael@0 | 798 | } |
michael@0 | 799 | |
michael@0 | 800 | return js::Wrapper::defineProperty(cx, proxy, id, desc); |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | bool |
michael@0 | 804 | nsOuterWindowProxy::getOwnPropertyNames(JSContext *cx, |
michael@0 | 805 | JS::Handle<JSObject*> proxy, |
michael@0 | 806 | JS::AutoIdVector &props) |
michael@0 | 807 | { |
michael@0 | 808 | // Just our indexed stuff followed by our "normal" own property names. |
michael@0 | 809 | if (!AppendIndexedPropertyNames(cx, proxy, props)) { |
michael@0 | 810 | return false; |
michael@0 | 811 | } |
michael@0 | 812 | |
michael@0 | 813 | JS::AutoIdVector innerProps(cx); |
michael@0 | 814 | if (!js::Wrapper::getOwnPropertyNames(cx, proxy, innerProps)) { |
michael@0 | 815 | return false; |
michael@0 | 816 | } |
michael@0 | 817 | return js::AppendUnique(cx, props, innerProps); |
michael@0 | 818 | } |
michael@0 | 819 | |
michael@0 | 820 | bool |
michael@0 | 821 | nsOuterWindowProxy::delete_(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 822 | JS::Handle<jsid> id, bool *bp) |
michael@0 | 823 | { |
michael@0 | 824 | if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) { |
michael@0 | 825 | // Reject (which means throw if strict, else return false) the delete. |
michael@0 | 826 | // Except we don't even know whether we're strict. See bug 803157. |
michael@0 | 827 | *bp = false; |
michael@0 | 828 | return true; |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | int32_t index = GetArrayIndexFromId(cx, id); |
michael@0 | 832 | if (IsArrayIndex(index)) { |
michael@0 | 833 | // Indexed, but not supported. Spec says return true. |
michael@0 | 834 | *bp = true; |
michael@0 | 835 | return true; |
michael@0 | 836 | } |
michael@0 | 837 | |
michael@0 | 838 | return js::Wrapper::delete_(cx, proxy, id, bp); |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | bool |
michael@0 | 842 | nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 843 | JS::AutoIdVector &props) |
michael@0 | 844 | { |
michael@0 | 845 | // Just our indexed stuff followed by our "normal" own property names. |
michael@0 | 846 | if (!AppendIndexedPropertyNames(cx, proxy, props)) { |
michael@0 | 847 | return false; |
michael@0 | 848 | } |
michael@0 | 849 | |
michael@0 | 850 | JS::AutoIdVector innerProps(cx); |
michael@0 | 851 | if (!js::Wrapper::enumerate(cx, proxy, innerProps)) { |
michael@0 | 852 | return false; |
michael@0 | 853 | } |
michael@0 | 854 | return js::AppendUnique(cx, props, innerProps); |
michael@0 | 855 | } |
michael@0 | 856 | |
michael@0 | 857 | bool |
michael@0 | 858 | nsOuterWindowProxy::has(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 859 | JS::Handle<jsid> id, bool *bp) |
michael@0 | 860 | { |
michael@0 | 861 | if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) { |
michael@0 | 862 | *bp = true; |
michael@0 | 863 | return true; |
michael@0 | 864 | } |
michael@0 | 865 | |
michael@0 | 866 | return js::Wrapper::has(cx, proxy, id, bp); |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | bool |
michael@0 | 870 | nsOuterWindowProxy::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 871 | JS::Handle<jsid> id, bool *bp) |
michael@0 | 872 | { |
michael@0 | 873 | if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) { |
michael@0 | 874 | *bp = true; |
michael@0 | 875 | return true; |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | return js::Wrapper::hasOwn(cx, proxy, id, bp); |
michael@0 | 879 | } |
michael@0 | 880 | |
michael@0 | 881 | bool |
michael@0 | 882 | nsOuterWindowProxy::get(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 883 | JS::Handle<JSObject*> receiver, |
michael@0 | 884 | JS::Handle<jsid> id, |
michael@0 | 885 | JS::MutableHandle<JS::Value> vp) |
michael@0 | 886 | { |
michael@0 | 887 | if (id == nsDOMClassInfo::sWrappedJSObject_id && |
michael@0 | 888 | xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { |
michael@0 | 889 | vp.set(JS::ObjectValue(*proxy)); |
michael@0 | 890 | return true; |
michael@0 | 891 | } |
michael@0 | 892 | |
michael@0 | 893 | bool found; |
michael@0 | 894 | if (!GetSubframeWindow(cx, proxy, id, vp, found)) { |
michael@0 | 895 | return false; |
michael@0 | 896 | } |
michael@0 | 897 | if (found) { |
michael@0 | 898 | return true; |
michael@0 | 899 | } |
michael@0 | 900 | // Else fall through to js::Wrapper |
michael@0 | 901 | |
michael@0 | 902 | return js::Wrapper::get(cx, proxy, receiver, id, vp); |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | bool |
michael@0 | 906 | nsOuterWindowProxy::set(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 907 | JS::Handle<JSObject*> receiver, |
michael@0 | 908 | JS::Handle<jsid> id, |
michael@0 | 909 | bool strict, |
michael@0 | 910 | JS::MutableHandle<JS::Value> vp) |
michael@0 | 911 | { |
michael@0 | 912 | int32_t index = GetArrayIndexFromId(cx, id); |
michael@0 | 913 | if (IsArrayIndex(index)) { |
michael@0 | 914 | // Reject (which means throw if and only if strict) the set. |
michael@0 | 915 | if (strict) { |
michael@0 | 916 | // XXXbz This needs to throw, but see bug 828137. |
michael@0 | 917 | } |
michael@0 | 918 | return true; |
michael@0 | 919 | } |
michael@0 | 920 | |
michael@0 | 921 | return js::Wrapper::set(cx, proxy, receiver, id, strict, vp); |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | bool |
michael@0 | 925 | nsOuterWindowProxy::keys(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 926 | JS::AutoIdVector &props) |
michael@0 | 927 | { |
michael@0 | 928 | // BaseProxyHandler::keys seems to do what we want here: call |
michael@0 | 929 | // getOwnPropertyNames and then filter out the non-enumerable properties. |
michael@0 | 930 | return js::BaseProxyHandler::keys(cx, proxy, props); |
michael@0 | 931 | } |
michael@0 | 932 | |
michael@0 | 933 | bool |
michael@0 | 934 | nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 935 | unsigned flags, JS::MutableHandle<JS::Value> vp) |
michael@0 | 936 | { |
michael@0 | 937 | // BaseProxyHandler::iterate seems to do what we want here: fall |
michael@0 | 938 | // back on the property names returned from keys() and enumerate(). |
michael@0 | 939 | return js::BaseProxyHandler::iterate(cx, proxy, flags, vp); |
michael@0 | 940 | } |
michael@0 | 941 | |
michael@0 | 942 | bool |
michael@0 | 943 | nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, |
michael@0 | 944 | JS::Handle<JSObject*> proxy, |
michael@0 | 945 | JS::Handle<jsid> id, |
michael@0 | 946 | JS::MutableHandle<JS::Value> vp, |
michael@0 | 947 | bool& found) |
michael@0 | 948 | { |
michael@0 | 949 | nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id); |
michael@0 | 950 | if (!frame) { |
michael@0 | 951 | found = false; |
michael@0 | 952 | return true; |
michael@0 | 953 | } |
michael@0 | 954 | |
michael@0 | 955 | found = true; |
michael@0 | 956 | // Just return the window's global |
michael@0 | 957 | nsGlobalWindow* global = static_cast<nsGlobalWindow*>(frame.get()); |
michael@0 | 958 | global->EnsureInnerWindow(); |
michael@0 | 959 | JSObject* obj = global->FastGetGlobalJSObject(); |
michael@0 | 960 | // This null check fixes a hard-to-reproduce crash that occurs when we |
michael@0 | 961 | // get here when we're mid-call to nsDocShell::Destroy. See bug 640904 |
michael@0 | 962 | // comment 105. |
michael@0 | 963 | if (MOZ_UNLIKELY(!obj)) { |
michael@0 | 964 | return xpc::Throw(cx, NS_ERROR_FAILURE); |
michael@0 | 965 | } |
michael@0 | 966 | |
michael@0 | 967 | vp.setObject(*obj); |
michael@0 | 968 | return JS_WrapValue(cx, vp); |
michael@0 | 969 | } |
michael@0 | 970 | |
michael@0 | 971 | already_AddRefed<nsIDOMWindow> |
michael@0 | 972 | nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, |
michael@0 | 973 | JS::Handle<JSObject*> proxy, |
michael@0 | 974 | JS::Handle<jsid> id) |
michael@0 | 975 | { |
michael@0 | 976 | int32_t index = GetArrayIndexFromId(cx, id); |
michael@0 | 977 | if (!IsArrayIndex(index)) { |
michael@0 | 978 | return nullptr; |
michael@0 | 979 | } |
michael@0 | 980 | |
michael@0 | 981 | nsGlobalWindow* win = GetWindow(proxy); |
michael@0 | 982 | bool unused; |
michael@0 | 983 | return win->IndexedGetter(index, unused); |
michael@0 | 984 | } |
michael@0 | 985 | |
michael@0 | 986 | bool |
michael@0 | 987 | nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, |
michael@0 | 988 | JS::AutoIdVector &props) |
michael@0 | 989 | { |
michael@0 | 990 | uint32_t length = GetWindow(proxy)->Length(); |
michael@0 | 991 | MOZ_ASSERT(int32_t(length) >= 0); |
michael@0 | 992 | if (!props.reserve(props.length() + length)) { |
michael@0 | 993 | return false; |
michael@0 | 994 | } |
michael@0 | 995 | for (int32_t i = 0; i < int32_t(length); ++i) { |
michael@0 | 996 | props.append(INT_TO_JSID(i)); |
michael@0 | 997 | } |
michael@0 | 998 | |
michael@0 | 999 | return true; |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | bool |
michael@0 | 1003 | nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 1004 | JS::Handle<jsid> id, JS::Handle<JSObject*> callable) |
michael@0 | 1005 | { |
michael@0 | 1006 | return js::WatchGuts(cx, proxy, id, callable); |
michael@0 | 1007 | } |
michael@0 | 1008 | |
michael@0 | 1009 | bool |
michael@0 | 1010 | nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy, |
michael@0 | 1011 | JS::Handle<jsid> id) |
michael@0 | 1012 | { |
michael@0 | 1013 | return js::UnwatchGuts(cx, proxy, id); |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | nsOuterWindowProxy |
michael@0 | 1017 | nsOuterWindowProxy::singleton; |
michael@0 | 1018 | |
michael@0 | 1019 | class nsChromeOuterWindowProxy : public nsOuterWindowProxy |
michael@0 | 1020 | { |
michael@0 | 1021 | public: |
michael@0 | 1022 | nsChromeOuterWindowProxy() : nsOuterWindowProxy() {} |
michael@0 | 1023 | |
michael@0 | 1024 | virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE; |
michael@0 | 1025 | |
michael@0 | 1026 | static nsChromeOuterWindowProxy singleton; |
michael@0 | 1027 | }; |
michael@0 | 1028 | |
michael@0 | 1029 | const char * |
michael@0 | 1030 | nsChromeOuterWindowProxy::className(JSContext *cx, |
michael@0 | 1031 | JS::Handle<JSObject*> proxy) |
michael@0 | 1032 | { |
michael@0 | 1033 | MOZ_ASSERT(js::IsProxy(proxy)); |
michael@0 | 1034 | |
michael@0 | 1035 | return "ChromeWindow"; |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | nsChromeOuterWindowProxy |
michael@0 | 1039 | nsChromeOuterWindowProxy::singleton; |
michael@0 | 1040 | |
michael@0 | 1041 | static JSObject* |
michael@0 | 1042 | NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> parent, bool isChrome) |
michael@0 | 1043 | { |
michael@0 | 1044 | JSAutoCompartment ac(cx, parent); |
michael@0 | 1045 | js::WrapperOptions options; |
michael@0 | 1046 | options.setClass(&OuterWindowProxyClass); |
michael@0 | 1047 | options.setSingleton(true); |
michael@0 | 1048 | JSObject *obj = js::Wrapper::New(cx, parent, parent, |
michael@0 | 1049 | isChrome ? &nsChromeOuterWindowProxy::singleton |
michael@0 | 1050 | : &nsOuterWindowProxy::singleton, |
michael@0 | 1051 | &options); |
michael@0 | 1052 | |
michael@0 | 1053 | NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class"); |
michael@0 | 1054 | return obj; |
michael@0 | 1055 | } |
michael@0 | 1056 | |
michael@0 | 1057 | //***************************************************************************** |
michael@0 | 1058 | //*** nsGlobalWindow: Object Management |
michael@0 | 1059 | //***************************************************************************** |
michael@0 | 1060 | |
michael@0 | 1061 | nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) |
michael@0 | 1062 | : nsPIDOMWindow(aOuterWindow), |
michael@0 | 1063 | mIdleFuzzFactor(0), |
michael@0 | 1064 | mIdleCallbackIndex(-1), |
michael@0 | 1065 | mCurrentlyIdle(false), |
michael@0 | 1066 | mAddActiveEventFuzzTime(true), |
michael@0 | 1067 | mIsFrozen(false), |
michael@0 | 1068 | mFullScreen(false), |
michael@0 | 1069 | mIsClosed(false), |
michael@0 | 1070 | mInClose(false), |
michael@0 | 1071 | mHavePendingClose(false), |
michael@0 | 1072 | mHadOriginalOpener(false), |
michael@0 | 1073 | mIsPopupSpam(false), |
michael@0 | 1074 | mBlockScriptedClosingFlag(false), |
michael@0 | 1075 | mFireOfflineStatusChangeEventOnThaw(false), |
michael@0 | 1076 | mNotifyIdleObserversIdleOnThaw(false), |
michael@0 | 1077 | mNotifyIdleObserversActiveOnThaw(false), |
michael@0 | 1078 | mCreatingInnerWindow(false), |
michael@0 | 1079 | mIsChrome(false), |
michael@0 | 1080 | mCleanMessageManager(false), |
michael@0 | 1081 | mNeedsFocus(true), |
michael@0 | 1082 | mHasFocus(false), |
michael@0 | 1083 | #if defined(XP_MACOSX) |
michael@0 | 1084 | mShowAccelerators(false), |
michael@0 | 1085 | mShowFocusRings(false), |
michael@0 | 1086 | #else |
michael@0 | 1087 | mShowAccelerators(true), |
michael@0 | 1088 | mShowFocusRings(true), |
michael@0 | 1089 | #endif |
michael@0 | 1090 | mShowFocusRingForContent(false), |
michael@0 | 1091 | mFocusByKeyOccurred(false), |
michael@0 | 1092 | mInnerObjectsFreed(false), |
michael@0 | 1093 | mHasGamepad(false), |
michael@0 | 1094 | #ifdef MOZ_GAMEPAD |
michael@0 | 1095 | mHasSeenGamepadInput(false), |
michael@0 | 1096 | #endif |
michael@0 | 1097 | mNotifiedIDDestroyed(false), |
michael@0 | 1098 | mAllowScriptsToClose(false), |
michael@0 | 1099 | mTimeoutInsertionPoint(nullptr), |
michael@0 | 1100 | mTimeoutPublicIdCounter(1), |
michael@0 | 1101 | mTimeoutFiringDepth(0), |
michael@0 | 1102 | mTimeoutsSuspendDepth(0), |
michael@0 | 1103 | mFocusMethod(0), |
michael@0 | 1104 | mSerial(0), |
michael@0 | 1105 | #ifdef DEBUG |
michael@0 | 1106 | mSetOpenerWindowCalled(false), |
michael@0 | 1107 | #endif |
michael@0 | 1108 | #ifdef MOZ_B2G |
michael@0 | 1109 | mNetworkUploadObserverEnabled(false), |
michael@0 | 1110 | mNetworkDownloadObserverEnabled(false), |
michael@0 | 1111 | #endif |
michael@0 | 1112 | mCleanedUp(false), |
michael@0 | 1113 | mDialogAbuseCount(0), |
michael@0 | 1114 | mAreDialogsEnabled(true) |
michael@0 | 1115 | { |
michael@0 | 1116 | nsLayoutStatics::AddRef(); |
michael@0 | 1117 | |
michael@0 | 1118 | // Initialize the PRCList (this). |
michael@0 | 1119 | PR_INIT_CLIST(this); |
michael@0 | 1120 | |
michael@0 | 1121 | if (aOuterWindow) { |
michael@0 | 1122 | // |this| is an inner window, add this inner window to the outer |
michael@0 | 1123 | // window list of inners. |
michael@0 | 1124 | PR_INSERT_AFTER(this, aOuterWindow); |
michael@0 | 1125 | |
michael@0 | 1126 | mObserver = new nsGlobalWindowObserver(this); |
michael@0 | 1127 | if (mObserver) { |
michael@0 | 1128 | NS_ADDREF(mObserver); |
michael@0 | 1129 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 1130 | if (os) { |
michael@0 | 1131 | // Watch for online/offline status changes so we can fire events. Use |
michael@0 | 1132 | // a strong reference. |
michael@0 | 1133 | os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, |
michael@0 | 1134 | false); |
michael@0 | 1135 | |
michael@0 | 1136 | // Watch for dom-storage2-changed so we can fire storage |
michael@0 | 1137 | // events. Use a strong reference. |
michael@0 | 1138 | os->AddObserver(mObserver, "dom-storage2-changed", false); |
michael@0 | 1139 | } |
michael@0 | 1140 | } |
michael@0 | 1141 | } else { |
michael@0 | 1142 | // |this| is an outer window. Outer windows start out frozen and |
michael@0 | 1143 | // remain frozen until they get an inner window, so freeze this |
michael@0 | 1144 | // outer window here. |
michael@0 | 1145 | Freeze(); |
michael@0 | 1146 | |
michael@0 | 1147 | mObserver = nullptr; |
michael@0 | 1148 | SetIsDOMBinding(); |
michael@0 | 1149 | } |
michael@0 | 1150 | |
michael@0 | 1151 | // We could have failed the first time through trying |
michael@0 | 1152 | // to create the entropy collector, so we should |
michael@0 | 1153 | // try to get one until we succeed. |
michael@0 | 1154 | |
michael@0 | 1155 | gRefCnt++; |
michael@0 | 1156 | |
michael@0 | 1157 | if (gRefCnt == 1) { |
michael@0 | 1158 | Preferences::AddIntVarCache(&gMinTimeoutValue, |
michael@0 | 1159 | "dom.min_timeout_value", |
michael@0 | 1160 | DEFAULT_MIN_TIMEOUT_VALUE); |
michael@0 | 1161 | Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue, |
michael@0 | 1162 | "dom.min_background_timeout_value", |
michael@0 | 1163 | DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE); |
michael@0 | 1164 | Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled, |
michael@0 | 1165 | "dom.idle-observers-api.fuzz_time.disabled", |
michael@0 | 1166 | false); |
michael@0 | 1167 | } |
michael@0 | 1168 | |
michael@0 | 1169 | if (gDumpFile == nullptr) { |
michael@0 | 1170 | const nsAdoptingCString& fname = |
michael@0 | 1171 | Preferences::GetCString("browser.dom.window.dump.file"); |
michael@0 | 1172 | if (!fname.IsEmpty()) { |
michael@0 | 1173 | // if this fails to open, Dump() knows to just go to stdout |
michael@0 | 1174 | // on null. |
michael@0 | 1175 | gDumpFile = fopen(fname, "wb+"); |
michael@0 | 1176 | } else { |
michael@0 | 1177 | gDumpFile = stdout; |
michael@0 | 1178 | } |
michael@0 | 1179 | } |
michael@0 | 1180 | |
michael@0 | 1181 | mSerial = ++gSerialCounter; |
michael@0 | 1182 | |
michael@0 | 1183 | #ifdef DEBUG |
michael@0 | 1184 | if (!PR_GetEnv("MOZ_QUIET")) { |
michael@0 | 1185 | printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n", |
michael@0 | 1186 | gRefCnt, |
michael@0 | 1187 | static_cast<void*>(ToCanonicalSupports(this)), |
michael@0 | 1188 | getpid(), |
michael@0 | 1189 | gSerialCounter, |
michael@0 | 1190 | static_cast<void*>(ToCanonicalSupports(aOuterWindow))); |
michael@0 | 1191 | } |
michael@0 | 1192 | #endif |
michael@0 | 1193 | |
michael@0 | 1194 | #ifdef PR_LOGGING |
michael@0 | 1195 | if (gDOMLeakPRLog) |
michael@0 | 1196 | PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, |
michael@0 | 1197 | ("DOMWINDOW %p created outer=%p", this, aOuterWindow)); |
michael@0 | 1198 | #endif |
michael@0 | 1199 | |
michael@0 | 1200 | NS_ASSERTION(sWindowsById, "Windows hash table must be created!"); |
michael@0 | 1201 | NS_ASSERTION(!sWindowsById->Get(mWindowID), |
michael@0 | 1202 | "This window shouldn't be in the hash table yet!"); |
michael@0 | 1203 | // We seem to see crashes in release builds because of null |sWindowsById|. |
michael@0 | 1204 | if (sWindowsById) { |
michael@0 | 1205 | sWindowsById->Put(mWindowID, this); |
michael@0 | 1206 | } |
michael@0 | 1207 | } |
michael@0 | 1208 | |
michael@0 | 1209 | /* static */ |
michael@0 | 1210 | void |
michael@0 | 1211 | nsGlobalWindow::Init() |
michael@0 | 1212 | { |
michael@0 | 1213 | CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector); |
michael@0 | 1214 | NS_ASSERTION(gEntropyCollector, |
michael@0 | 1215 | "gEntropyCollector should have been initialized!"); |
michael@0 | 1216 | |
michael@0 | 1217 | #ifdef PR_LOGGING |
michael@0 | 1218 | gDOMLeakPRLog = PR_NewLogModule("DOMLeak"); |
michael@0 | 1219 | NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!"); |
michael@0 | 1220 | #endif |
michael@0 | 1221 | |
michael@0 | 1222 | sWindowsById = new WindowByIdTable(); |
michael@0 | 1223 | } |
michael@0 | 1224 | |
michael@0 | 1225 | static PLDHashOperator |
michael@0 | 1226 | DisconnectEventTargetObjects(nsPtrHashKey<DOMEventTargetHelper>* aKey, |
michael@0 | 1227 | void* aClosure) |
michael@0 | 1228 | { |
michael@0 | 1229 | nsRefPtr<DOMEventTargetHelper> target = aKey->GetKey(); |
michael@0 | 1230 | target->DisconnectFromOwner(); |
michael@0 | 1231 | return PL_DHASH_NEXT; |
michael@0 | 1232 | } |
michael@0 | 1233 | |
michael@0 | 1234 | nsGlobalWindow::~nsGlobalWindow() |
michael@0 | 1235 | { |
michael@0 | 1236 | mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr); |
michael@0 | 1237 | mEventTargetObjects.Clear(); |
michael@0 | 1238 | |
michael@0 | 1239 | // We have to check if sWindowsById isn't null because ::Shutdown might have |
michael@0 | 1240 | // been called. |
michael@0 | 1241 | if (sWindowsById) { |
michael@0 | 1242 | NS_ASSERTION(sWindowsById->Get(mWindowID), |
michael@0 | 1243 | "This window should be in the hash table"); |
michael@0 | 1244 | sWindowsById->Remove(mWindowID); |
michael@0 | 1245 | } |
michael@0 | 1246 | |
michael@0 | 1247 | --gRefCnt; |
michael@0 | 1248 | |
michael@0 | 1249 | #ifdef DEBUG |
michael@0 | 1250 | if (!PR_GetEnv("MOZ_QUIET")) { |
michael@0 | 1251 | nsAutoCString url; |
michael@0 | 1252 | if (mLastOpenedURI) { |
michael@0 | 1253 | mLastOpenedURI->GetSpec(url); |
michael@0 | 1254 | |
michael@0 | 1255 | // Data URLs can be very long, so truncate to avoid flooding the log. |
michael@0 | 1256 | const uint32_t maxURLLength = 1000; |
michael@0 | 1257 | if (url.Length() > maxURLLength) { |
michael@0 | 1258 | url.Truncate(maxURLLength); |
michael@0 | 1259 | } |
michael@0 | 1260 | } |
michael@0 | 1261 | |
michael@0 | 1262 | nsGlobalWindow* outer = static_cast<nsGlobalWindow*>(mOuterWindow.get()); |
michael@0 | 1263 | printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n", |
michael@0 | 1264 | gRefCnt, |
michael@0 | 1265 | static_cast<void*>(ToCanonicalSupports(this)), |
michael@0 | 1266 | getpid(), |
michael@0 | 1267 | mSerial, |
michael@0 | 1268 | static_cast<void*>(ToCanonicalSupports(outer)), |
michael@0 | 1269 | url.get()); |
michael@0 | 1270 | } |
michael@0 | 1271 | #endif |
michael@0 | 1272 | |
michael@0 | 1273 | #ifdef PR_LOGGING |
michael@0 | 1274 | if (gDOMLeakPRLog) |
michael@0 | 1275 | PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, |
michael@0 | 1276 | ("DOMWINDOW %p destroyed", this)); |
michael@0 | 1277 | #endif |
michael@0 | 1278 | |
michael@0 | 1279 | if (IsOuterWindow()) { |
michael@0 | 1280 | JSObject *proxy = GetWrapperPreserveColor(); |
michael@0 | 1281 | if (proxy) { |
michael@0 | 1282 | js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr)); |
michael@0 | 1283 | } |
michael@0 | 1284 | |
michael@0 | 1285 | // An outer window is destroyed with inner windows still possibly |
michael@0 | 1286 | // alive, iterate through the inner windows and null out their |
michael@0 | 1287 | // back pointer to this outer, and pull them out of the list of |
michael@0 | 1288 | // inner windows. |
michael@0 | 1289 | |
michael@0 | 1290 | nsGlobalWindow *w; |
michael@0 | 1291 | while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) { |
michael@0 | 1292 | PR_REMOVE_AND_INIT_LINK(w); |
michael@0 | 1293 | } |
michael@0 | 1294 | |
michael@0 | 1295 | DropOuterWindowDocs(); |
michael@0 | 1296 | } else { |
michael@0 | 1297 | Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, |
michael@0 | 1298 | mMutationBits ? 1 : 0); |
michael@0 | 1299 | |
michael@0 | 1300 | if (mListenerManager) { |
michael@0 | 1301 | mListenerManager->Disconnect(); |
michael@0 | 1302 | mListenerManager = nullptr; |
michael@0 | 1303 | } |
michael@0 | 1304 | |
michael@0 | 1305 | // An inner window is destroyed, pull it out of the outer window's |
michael@0 | 1306 | // list if inner windows. |
michael@0 | 1307 | |
michael@0 | 1308 | PR_REMOVE_LINK(this); |
michael@0 | 1309 | |
michael@0 | 1310 | // If our outer window's inner window is this window, null out the |
michael@0 | 1311 | // outer window's reference to this window that's being deleted. |
michael@0 | 1312 | nsGlobalWindow *outer = GetOuterWindowInternal(); |
michael@0 | 1313 | if (outer) { |
michael@0 | 1314 | outer->MaybeClearInnerWindow(this); |
michael@0 | 1315 | } |
michael@0 | 1316 | } |
michael@0 | 1317 | |
michael@0 | 1318 | // Outer windows are always supposed to call CleanUp before letting themselves |
michael@0 | 1319 | // be destroyed. And while CleanUp generally seems to be intended to clean up |
michael@0 | 1320 | // outers, we've historically called it for both. Changing this would probably |
michael@0 | 1321 | // involve auditing all of the references that inners and outers can have, and |
michael@0 | 1322 | // separating the handling into CleanUp() and FreeInnerObjects. |
michael@0 | 1323 | if (IsInnerWindow()) { |
michael@0 | 1324 | CleanUp(); |
michael@0 | 1325 | } else { |
michael@0 | 1326 | MOZ_ASSERT(mCleanedUp); |
michael@0 | 1327 | } |
michael@0 | 1328 | |
michael@0 | 1329 | nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); |
michael@0 | 1330 | if (ac) |
michael@0 | 1331 | ac->RemoveWindowAsListener(this); |
michael@0 | 1332 | |
michael@0 | 1333 | nsLayoutStatics::Release(); |
michael@0 | 1334 | } |
michael@0 | 1335 | |
michael@0 | 1336 | void |
michael@0 | 1337 | nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject) |
michael@0 | 1338 | { |
michael@0 | 1339 | mEventTargetObjects.PutEntry(aObject); |
michael@0 | 1340 | } |
michael@0 | 1341 | |
michael@0 | 1342 | void |
michael@0 | 1343 | nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject) |
michael@0 | 1344 | { |
michael@0 | 1345 | mEventTargetObjects.RemoveEntry(aObject); |
michael@0 | 1346 | } |
michael@0 | 1347 | |
michael@0 | 1348 | // static |
michael@0 | 1349 | void |
michael@0 | 1350 | nsGlobalWindow::ShutDown() |
michael@0 | 1351 | { |
michael@0 | 1352 | if (gDumpFile && gDumpFile != stdout) { |
michael@0 | 1353 | fclose(gDumpFile); |
michael@0 | 1354 | } |
michael@0 | 1355 | gDumpFile = nullptr; |
michael@0 | 1356 | |
michael@0 | 1357 | NS_IF_RELEASE(gEntropyCollector); |
michael@0 | 1358 | |
michael@0 | 1359 | delete sWindowsById; |
michael@0 | 1360 | sWindowsById = nullptr; |
michael@0 | 1361 | } |
michael@0 | 1362 | |
michael@0 | 1363 | // static |
michael@0 | 1364 | void |
michael@0 | 1365 | nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow) |
michael@0 | 1366 | { |
michael@0 | 1367 | if (aWindow->mCachedXBLPrototypeHandlers && |
michael@0 | 1368 | aWindow->mCachedXBLPrototypeHandlers->Count() > 0) { |
michael@0 | 1369 | aWindow->mCachedXBLPrototypeHandlers->Clear(); |
michael@0 | 1370 | } |
michael@0 | 1371 | } |
michael@0 | 1372 | |
michael@0 | 1373 | void |
michael@0 | 1374 | nsGlobalWindow::MaybeForgiveSpamCount() |
michael@0 | 1375 | { |
michael@0 | 1376 | if (IsOuterWindow() && |
michael@0 | 1377 | IsPopupSpamWindow()) |
michael@0 | 1378 | { |
michael@0 | 1379 | SetPopupSpamWindow(false); |
michael@0 | 1380 | --gOpenPopupSpamCount; |
michael@0 | 1381 | NS_ASSERTION(gOpenPopupSpamCount >= 0, |
michael@0 | 1382 | "Unbalanced decrement of gOpenPopupSpamCount"); |
michael@0 | 1383 | } |
michael@0 | 1384 | } |
michael@0 | 1385 | |
michael@0 | 1386 | void |
michael@0 | 1387 | nsGlobalWindow::DropOuterWindowDocs() |
michael@0 | 1388 | { |
michael@0 | 1389 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 1390 | MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed()); |
michael@0 | 1391 | mDoc = nullptr; |
michael@0 | 1392 | mSuspendedDoc = nullptr; |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | void |
michael@0 | 1396 | nsGlobalWindow::CleanUp() |
michael@0 | 1397 | { |
michael@0 | 1398 | // Guarantee idempotence. |
michael@0 | 1399 | if (mCleanedUp) |
michael@0 | 1400 | return; |
michael@0 | 1401 | mCleanedUp = true; |
michael@0 | 1402 | |
michael@0 | 1403 | mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr); |
michael@0 | 1404 | mEventTargetObjects.Clear(); |
michael@0 | 1405 | |
michael@0 | 1406 | if (mObserver) { |
michael@0 | 1407 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 1408 | if (os) { |
michael@0 | 1409 | os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); |
michael@0 | 1410 | os->RemoveObserver(mObserver, "dom-storage2-changed"); |
michael@0 | 1411 | } |
michael@0 | 1412 | |
michael@0 | 1413 | #ifdef MOZ_B2G |
michael@0 | 1414 | DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); |
michael@0 | 1415 | DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); |
michael@0 | 1416 | #endif // MOZ_B2G |
michael@0 | 1417 | |
michael@0 | 1418 | if (mIdleService) { |
michael@0 | 1419 | mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); |
michael@0 | 1420 | } |
michael@0 | 1421 | |
michael@0 | 1422 | // Drop its reference to this dying window, in case for some bogus reason |
michael@0 | 1423 | // the object stays around. |
michael@0 | 1424 | mObserver->Forget(); |
michael@0 | 1425 | NS_RELEASE(mObserver); |
michael@0 | 1426 | } |
michael@0 | 1427 | |
michael@0 | 1428 | if (mNavigator) { |
michael@0 | 1429 | mNavigator->Invalidate(); |
michael@0 | 1430 | mNavigator = nullptr; |
michael@0 | 1431 | } |
michael@0 | 1432 | |
michael@0 | 1433 | mScreen = nullptr; |
michael@0 | 1434 | mMenubar = nullptr; |
michael@0 | 1435 | mToolbar = nullptr; |
michael@0 | 1436 | mLocationbar = nullptr; |
michael@0 | 1437 | mPersonalbar = nullptr; |
michael@0 | 1438 | mStatusbar = nullptr; |
michael@0 | 1439 | mScrollbars = nullptr; |
michael@0 | 1440 | mLocation = nullptr; |
michael@0 | 1441 | mHistory = nullptr; |
michael@0 | 1442 | mFrames = nullptr; |
michael@0 | 1443 | mWindowUtils = nullptr; |
michael@0 | 1444 | mApplicationCache = nullptr; |
michael@0 | 1445 | mIndexedDB = nullptr; |
michael@0 | 1446 | |
michael@0 | 1447 | mConsole = nullptr; |
michael@0 | 1448 | |
michael@0 | 1449 | mExternal = nullptr; |
michael@0 | 1450 | |
michael@0 | 1451 | mPerformance = nullptr; |
michael@0 | 1452 | |
michael@0 | 1453 | #ifdef MOZ_WEBSPEECH |
michael@0 | 1454 | mSpeechSynthesis = nullptr; |
michael@0 | 1455 | #endif |
michael@0 | 1456 | |
michael@0 | 1457 | ClearControllers(); |
michael@0 | 1458 | |
michael@0 | 1459 | mOpener = nullptr; // Forces Release |
michael@0 | 1460 | if (mContext) { |
michael@0 | 1461 | mContext = nullptr; // Forces Release |
michael@0 | 1462 | } |
michael@0 | 1463 | mChromeEventHandler = nullptr; // Forces Release |
michael@0 | 1464 | mParentTarget = nullptr; |
michael@0 | 1465 | |
michael@0 | 1466 | nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); |
michael@0 | 1467 | |
michael@0 | 1468 | if (inner) { |
michael@0 | 1469 | inner->CleanUp(); |
michael@0 | 1470 | } |
michael@0 | 1471 | |
michael@0 | 1472 | DisableGamepadUpdates(); |
michael@0 | 1473 | mHasGamepad = false; |
michael@0 | 1474 | |
michael@0 | 1475 | if (mCleanMessageManager) { |
michael@0 | 1476 | NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned"); |
michael@0 | 1477 | nsGlobalChromeWindow *asChrome = static_cast<nsGlobalChromeWindow*>(this); |
michael@0 | 1478 | if (asChrome->mMessageManager) { |
michael@0 | 1479 | static_cast<nsFrameMessageManager*>( |
michael@0 | 1480 | asChrome->mMessageManager.get())->Disconnect(); |
michael@0 | 1481 | } |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | mArguments = nullptr; |
michael@0 | 1485 | mDialogArguments = nullptr; |
michael@0 | 1486 | |
michael@0 | 1487 | CleanupCachedXBLHandlers(this); |
michael@0 | 1488 | |
michael@0 | 1489 | for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
michael@0 | 1490 | mAudioContexts[i]->Shutdown(); |
michael@0 | 1491 | } |
michael@0 | 1492 | mAudioContexts.Clear(); |
michael@0 | 1493 | |
michael@0 | 1494 | if (mIdleTimer) { |
michael@0 | 1495 | mIdleTimer->Cancel(); |
michael@0 | 1496 | mIdleTimer = nullptr; |
michael@0 | 1497 | } |
michael@0 | 1498 | |
michael@0 | 1499 | DisableTimeChangeNotifications(); |
michael@0 | 1500 | } |
michael@0 | 1501 | |
michael@0 | 1502 | void |
michael@0 | 1503 | nsGlobalWindow::ClearControllers() |
michael@0 | 1504 | { |
michael@0 | 1505 | if (mControllers) { |
michael@0 | 1506 | uint32_t count; |
michael@0 | 1507 | mControllers->GetControllerCount(&count); |
michael@0 | 1508 | |
michael@0 | 1509 | while (count--) { |
michael@0 | 1510 | nsCOMPtr<nsIController> controller; |
michael@0 | 1511 | mControllers->GetControllerAt(count, getter_AddRefs(controller)); |
michael@0 | 1512 | |
michael@0 | 1513 | nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller); |
michael@0 | 1514 | if (context) |
michael@0 | 1515 | context->SetCommandContext(nullptr); |
michael@0 | 1516 | } |
michael@0 | 1517 | |
michael@0 | 1518 | mControllers = nullptr; |
michael@0 | 1519 | } |
michael@0 | 1520 | } |
michael@0 | 1521 | |
michael@0 | 1522 | void |
michael@0 | 1523 | nsGlobalWindow::FreeInnerObjects() |
michael@0 | 1524 | { |
michael@0 | 1525 | NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); |
michael@0 | 1526 | |
michael@0 | 1527 | // Make sure that this is called before we null out the document and |
michael@0 | 1528 | // other members that the window destroyed observers could |
michael@0 | 1529 | // re-create. |
michael@0 | 1530 | NotifyDOMWindowDestroyed(this); |
michael@0 | 1531 | |
michael@0 | 1532 | mInnerObjectsFreed = true; |
michael@0 | 1533 | |
michael@0 | 1534 | // Kill all of the workers for this window. |
michael@0 | 1535 | mozilla::dom::workers::CancelWorkersForWindow(this); |
michael@0 | 1536 | |
michael@0 | 1537 | // Close all offline storages for this window. |
michael@0 | 1538 | quota::QuotaManager* quotaManager = quota::QuotaManager::Get(); |
michael@0 | 1539 | if (quotaManager) { |
michael@0 | 1540 | quotaManager->AbortCloseStoragesForWindow(this); |
michael@0 | 1541 | } |
michael@0 | 1542 | |
michael@0 | 1543 | ClearAllTimeouts(); |
michael@0 | 1544 | |
michael@0 | 1545 | if (mIdleTimer) { |
michael@0 | 1546 | mIdleTimer->Cancel(); |
michael@0 | 1547 | mIdleTimer = nullptr; |
michael@0 | 1548 | } |
michael@0 | 1549 | |
michael@0 | 1550 | mIdleObservers.Clear(); |
michael@0 | 1551 | |
michael@0 | 1552 | mChromeEventHandler = nullptr; |
michael@0 | 1553 | |
michael@0 | 1554 | if (mListenerManager) { |
michael@0 | 1555 | mListenerManager->Disconnect(); |
michael@0 | 1556 | mListenerManager = nullptr; |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | mLocation = nullptr; |
michael@0 | 1560 | mHistory = nullptr; |
michael@0 | 1561 | |
michael@0 | 1562 | if (mNavigator) { |
michael@0 | 1563 | mNavigator->OnNavigation(); |
michael@0 | 1564 | mNavigator->Invalidate(); |
michael@0 | 1565 | mNavigator = nullptr; |
michael@0 | 1566 | } |
michael@0 | 1567 | |
michael@0 | 1568 | if (mScreen) { |
michael@0 | 1569 | mScreen = nullptr; |
michael@0 | 1570 | } |
michael@0 | 1571 | |
michael@0 | 1572 | if (mDoc) { |
michael@0 | 1573 | // Remember the document's principal and URI. |
michael@0 | 1574 | mDocumentPrincipal = mDoc->NodePrincipal(); |
michael@0 | 1575 | mDocumentURI = mDoc->GetDocumentURI(); |
michael@0 | 1576 | mDocBaseURI = mDoc->GetDocBaseURI(); |
michael@0 | 1577 | |
michael@0 | 1578 | while (mDoc->EventHandlingSuppressed()) { |
michael@0 | 1579 | mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false); |
michael@0 | 1580 | } |
michael@0 | 1581 | |
michael@0 | 1582 | // Note: we don't have to worry about eAnimationsOnly suppressions because |
michael@0 | 1583 | // they won't leak. |
michael@0 | 1584 | } |
michael@0 | 1585 | |
michael@0 | 1586 | // Remove our reference to the document and the document principal. |
michael@0 | 1587 | mFocusedNode = nullptr; |
michael@0 | 1588 | |
michael@0 | 1589 | if (mApplicationCache) { |
michael@0 | 1590 | static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect(); |
michael@0 | 1591 | mApplicationCache = nullptr; |
michael@0 | 1592 | } |
michael@0 | 1593 | |
michael@0 | 1594 | mIndexedDB = nullptr; |
michael@0 | 1595 | |
michael@0 | 1596 | NotifyWindowIDDestroyed("inner-window-destroyed"); |
michael@0 | 1597 | |
michael@0 | 1598 | CleanupCachedXBLHandlers(this); |
michael@0 | 1599 | |
michael@0 | 1600 | for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
michael@0 | 1601 | mAudioContexts[i]->Shutdown(); |
michael@0 | 1602 | } |
michael@0 | 1603 | mAudioContexts.Clear(); |
michael@0 | 1604 | |
michael@0 | 1605 | #ifdef MOZ_GAMEPAD |
michael@0 | 1606 | DisableGamepadUpdates(); |
michael@0 | 1607 | mHasGamepad = false; |
michael@0 | 1608 | mGamepads.Clear(); |
michael@0 | 1609 | #endif |
michael@0 | 1610 | } |
michael@0 | 1611 | |
michael@0 | 1612 | //***************************************************************************** |
michael@0 | 1613 | // nsGlobalWindow::nsISupports |
michael@0 | 1614 | //***************************************************************************** |
michael@0 | 1615 | |
michael@0 | 1616 | DOMCI_DATA(Window, nsGlobalWindow) |
michael@0 | 1617 | |
michael@0 | 1618 | // QueryInterface implementation for nsGlobalWindow |
michael@0 | 1619 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) |
michael@0 | 1620 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
michael@0 | 1621 | // Make sure this matches the cast in nsGlobalWindow::FromWrapper() |
michael@0 | 1622 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget) |
michael@0 | 1623 | NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) |
michael@0 | 1624 | #ifdef MOZ_B2G |
michael@0 | 1625 | NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G) |
michael@0 | 1626 | #endif // MOZ_B2G |
michael@0 | 1627 | #ifdef MOZ_WEBSPEECH |
michael@0 | 1628 | NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter) |
michael@0 | 1629 | #endif // MOZ_B2G |
michael@0 | 1630 | NS_INTERFACE_MAP_ENTRY(nsIDOMJSWindow) |
michael@0 | 1631 | if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) { |
michael@0 | 1632 | foundInterface = static_cast<nsIDOMWindowInternal*>(this); |
michael@0 | 1633 | if (!sWarnedAboutWindowInternal) { |
michael@0 | 1634 | sWarnedAboutWindowInternal = true; |
michael@0 | 1635 | nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
michael@0 | 1636 | NS_LITERAL_CSTRING("Extensions"), mDoc, |
michael@0 | 1637 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 1638 | "nsIDOMWindowInternalWarning"); |
michael@0 | 1639 | } |
michael@0 | 1640 | } else |
michael@0 | 1641 | NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) |
michael@0 | 1642 | NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) |
michael@0 | 1643 | NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) |
michael@0 | 1644 | NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) |
michael@0 | 1645 | NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) |
michael@0 | 1646 | NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow) |
michael@0 | 1647 | NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
michael@0 | 1648 | NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) |
michael@0 | 1649 | NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance) |
michael@0 | 1650 | NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver) |
michael@0 | 1651 | NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers) |
michael@0 | 1652 | NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) |
michael@0 | 1653 | NS_INTERFACE_MAP_END |
michael@0 | 1654 | |
michael@0 | 1655 | |
michael@0 | 1656 | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) |
michael@0 | 1657 | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) |
michael@0 | 1658 | |
michael@0 | 1659 | static PLDHashOperator |
michael@0 | 1660 | MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) |
michael@0 | 1661 | { |
michael@0 | 1662 | JS::ExposeObjectToActiveJS(aData); |
michael@0 | 1663 | return PL_DHASH_NEXT; |
michael@0 | 1664 | } |
michael@0 | 1665 | |
michael@0 | 1666 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow) |
michael@0 | 1667 | if (tmp->IsBlackForCC(false)) { |
michael@0 | 1668 | if (tmp->mCachedXBLPrototypeHandlers) { |
michael@0 | 1669 | tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr); |
michael@0 | 1670 | } |
michael@0 | 1671 | if (EventListenerManager* elm = tmp->GetExistingListenerManager()) { |
michael@0 | 1672 | elm->MarkForCC(); |
michael@0 | 1673 | } |
michael@0 | 1674 | tmp->UnmarkGrayTimers(); |
michael@0 | 1675 | return true; |
michael@0 | 1676 | } |
michael@0 | 1677 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
michael@0 | 1678 | |
michael@0 | 1679 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow) |
michael@0 | 1680 | return tmp->IsBlackForCC(true); |
michael@0 | 1681 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
michael@0 | 1682 | |
michael@0 | 1683 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow) |
michael@0 | 1684 | return tmp->IsBlackForCC(false); |
michael@0 | 1685 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
michael@0 | 1686 | |
michael@0 | 1687 | inline void |
michael@0 | 1688 | ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, |
michael@0 | 1689 | IdleObserverHolder& aField, |
michael@0 | 1690 | const char* aName, |
michael@0 | 1691 | unsigned aFlags) |
michael@0 | 1692 | { |
michael@0 | 1693 | CycleCollectionNoteChild(aCallback, aField.mIdleObserver.get(), aName, aFlags); |
michael@0 | 1694 | } |
michael@0 | 1695 | |
michael@0 | 1696 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow) |
michael@0 | 1697 | |
michael@0 | 1698 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow) |
michael@0 | 1699 | if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
michael@0 | 1700 | char name[512]; |
michael@0 | 1701 | PR_snprintf(name, sizeof(name), "nsGlobalWindow #%ld", tmp->mWindowID); |
michael@0 | 1702 | cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
michael@0 | 1703 | } else { |
michael@0 | 1704 | NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get()) |
michael@0 | 1705 | } |
michael@0 | 1706 | |
michael@0 | 1707 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) |
michael@0 | 1708 | |
michael@0 | 1709 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) |
michael@0 | 1710 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) |
michael@0 | 1711 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments) |
michael@0 | 1712 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue) |
michael@0 | 1713 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) |
michael@0 | 1714 | |
michael@0 | 1715 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) |
michael@0 | 1716 | |
michael@0 | 1717 | #ifdef MOZ_WEBSPEECH |
michael@0 | 1718 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) |
michael@0 | 1719 | #endif |
michael@0 | 1720 | |
michael@0 | 1721 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) |
michael@0 | 1722 | |
michael@0 | 1723 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) |
michael@0 | 1724 | |
michael@0 | 1725 | for (nsTimeout* timeout = tmp->mTimeouts.getFirst(); |
michael@0 | 1726 | timeout; |
michael@0 | 1727 | timeout = timeout->getNext()) { |
michael@0 | 1728 | cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout)); |
michael@0 | 1729 | } |
michael@0 | 1730 | |
michael@0 | 1731 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) |
michael@0 | 1732 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage) |
michael@0 | 1733 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache) |
michael@0 | 1734 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) |
michael@0 | 1735 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) |
michael@0 | 1736 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService) |
michael@0 | 1737 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents) |
michael@0 | 1738 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers) |
michael@0 | 1739 | |
michael@0 | 1740 | #ifdef MOZ_GAMEPAD |
michael@0 | 1741 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads) |
michael@0 | 1742 | #endif |
michael@0 | 1743 | |
michael@0 | 1744 | // Traverse stuff from nsPIDOMWindow |
michael@0 | 1745 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) |
michael@0 | 1746 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) |
michael@0 | 1747 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) |
michael@0 | 1748 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode) |
michael@0 | 1749 | |
michael@0 | 1750 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar) |
michael@0 | 1751 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar) |
michael@0 | 1752 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar) |
michael@0 | 1753 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar) |
michael@0 | 1754 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar) |
michael@0 | 1755 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars) |
michael@0 | 1756 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) |
michael@0 | 1757 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) |
michael@0 | 1758 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal) |
michael@0 | 1759 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
michael@0 | 1760 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 1761 | |
michael@0 | 1762 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) |
michael@0 | 1763 | nsGlobalWindow::CleanupCachedXBLHandlers(tmp); |
michael@0 | 1764 | |
michael@0 | 1765 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) |
michael@0 | 1766 | |
michael@0 | 1767 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) |
michael@0 | 1768 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) |
michael@0 | 1769 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments) |
michael@0 | 1770 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue) |
michael@0 | 1771 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) |
michael@0 | 1772 | |
michael@0 | 1773 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) |
michael@0 | 1774 | |
michael@0 | 1775 | #ifdef MOZ_WEBSPEECH |
michael@0 | 1776 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) |
michael@0 | 1777 | #endif |
michael@0 | 1778 | |
michael@0 | 1779 | if (tmp->mOuterWindow) { |
michael@0 | 1780 | static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp); |
michael@0 | 1781 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) |
michael@0 | 1782 | } |
michael@0 | 1783 | |
michael@0 | 1784 | if (tmp->mListenerManager) { |
michael@0 | 1785 | tmp->mListenerManager->Disconnect(); |
michael@0 | 1786 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) |
michael@0 | 1787 | } |
michael@0 | 1788 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) |
michael@0 | 1789 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage) |
michael@0 | 1790 | if (tmp->mApplicationCache) { |
michael@0 | 1791 | static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect(); |
michael@0 | 1792 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache) |
michael@0 | 1793 | } |
michael@0 | 1794 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) |
michael@0 | 1795 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) |
michael@0 | 1796 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService) |
michael@0 | 1797 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents) |
michael@0 | 1798 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers) |
michael@0 | 1799 | |
michael@0 | 1800 | #ifdef MOZ_GAMEPAD |
michael@0 | 1801 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads) |
michael@0 | 1802 | #endif |
michael@0 | 1803 | |
michael@0 | 1804 | // Unlink stuff from nsPIDOMWindow |
michael@0 | 1805 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) |
michael@0 | 1806 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) |
michael@0 | 1807 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) |
michael@0 | 1808 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode) |
michael@0 | 1809 | |
michael@0 | 1810 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar) |
michael@0 | 1811 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar) |
michael@0 | 1812 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar) |
michael@0 | 1813 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar) |
michael@0 | 1814 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar) |
michael@0 | 1815 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars) |
michael@0 | 1816 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) |
michael@0 | 1817 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) |
michael@0 | 1818 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal) |
michael@0 | 1819 | NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
michael@0 | 1820 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 1821 | |
michael@0 | 1822 | #ifdef DEBUG |
michael@0 | 1823 | void |
michael@0 | 1824 | nsGlobalWindow::RiskyUnlink() |
michael@0 | 1825 | { |
michael@0 | 1826 | NS_CYCLE_COLLECTION_INNERNAME.Unlink(this); |
michael@0 | 1827 | } |
michael@0 | 1828 | #endif |
michael@0 | 1829 | |
michael@0 | 1830 | struct TraceData |
michael@0 | 1831 | { |
michael@0 | 1832 | const TraceCallbacks& callbacks; |
michael@0 | 1833 | void* closure; |
michael@0 | 1834 | }; |
michael@0 | 1835 | |
michael@0 | 1836 | static PLDHashOperator |
michael@0 | 1837 | TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) |
michael@0 | 1838 | { |
michael@0 | 1839 | TraceData* data = static_cast<TraceData*>(aClosure); |
michael@0 | 1840 | data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure); |
michael@0 | 1841 | return PL_DHASH_NEXT; |
michael@0 | 1842 | } |
michael@0 | 1843 | |
michael@0 | 1844 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) |
michael@0 | 1845 | if (tmp->mCachedXBLPrototypeHandlers) { |
michael@0 | 1846 | TraceData data = { aCallbacks, aClosure }; |
michael@0 | 1847 | tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data); |
michael@0 | 1848 | } |
michael@0 | 1849 | NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
michael@0 | 1850 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
michael@0 | 1851 | |
michael@0 | 1852 | bool |
michael@0 | 1853 | nsGlobalWindow::IsBlackForCC(bool aTracingNeeded) |
michael@0 | 1854 | { |
michael@0 | 1855 | if (!nsCCUncollectableMarker::sGeneration) { |
michael@0 | 1856 | return false; |
michael@0 | 1857 | } |
michael@0 | 1858 | |
michael@0 | 1859 | return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) || |
michael@0 | 1860 | IsBlack()) && |
michael@0 | 1861 | (!aTracingNeeded || |
michael@0 | 1862 | HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this))); |
michael@0 | 1863 | } |
michael@0 | 1864 | |
michael@0 | 1865 | void |
michael@0 | 1866 | nsGlobalWindow::UnmarkGrayTimers() |
michael@0 | 1867 | { |
michael@0 | 1868 | for (nsTimeout* timeout = mTimeouts.getFirst(); |
michael@0 | 1869 | timeout; |
michael@0 | 1870 | timeout = timeout->getNext()) { |
michael@0 | 1871 | if (timeout->mScriptHandler) { |
michael@0 | 1872 | Function* f = timeout->mScriptHandler->GetCallback(); |
michael@0 | 1873 | if (f) { |
michael@0 | 1874 | // Callable() already does xpc_UnmarkGrayObject. |
michael@0 | 1875 | DebugOnly<JS::Handle<JSObject*> > o = f->Callable(); |
michael@0 | 1876 | MOZ_ASSERT(!xpc_IsGrayGCThing(o.value), "Should have been unmarked"); |
michael@0 | 1877 | } |
michael@0 | 1878 | } |
michael@0 | 1879 | } |
michael@0 | 1880 | } |
michael@0 | 1881 | |
michael@0 | 1882 | //***************************************************************************** |
michael@0 | 1883 | // nsGlobalWindow::nsIScriptGlobalObject |
michael@0 | 1884 | //***************************************************************************** |
michael@0 | 1885 | |
michael@0 | 1886 | nsresult |
michael@0 | 1887 | nsGlobalWindow::EnsureScriptEnvironment() |
michael@0 | 1888 | { |
michael@0 | 1889 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 1890 | if (!outer) { |
michael@0 | 1891 | NS_WARNING("No outer window available!"); |
michael@0 | 1892 | return NS_ERROR_FAILURE; |
michael@0 | 1893 | } |
michael@0 | 1894 | |
michael@0 | 1895 | if (outer->GetWrapperPreserveColor()) { |
michael@0 | 1896 | return NS_OK; |
michael@0 | 1897 | } |
michael@0 | 1898 | |
michael@0 | 1899 | NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(), |
michael@0 | 1900 | "No cached wrapper, but we have an inner window?"); |
michael@0 | 1901 | |
michael@0 | 1902 | // If this window is a [i]frame, don't bother GC'ing when the frame's context |
michael@0 | 1903 | // is destroyed since a GC will happen when the frameset or host document is |
michael@0 | 1904 | // destroyed anyway. |
michael@0 | 1905 | nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer); |
michael@0 | 1906 | |
michael@0 | 1907 | NS_ASSERTION(!outer->mContext, "Will overwrite mContext!"); |
michael@0 | 1908 | |
michael@0 | 1909 | // should probably assert the context is clean??? |
michael@0 | 1910 | context->WillInitializeContext(); |
michael@0 | 1911 | |
michael@0 | 1912 | nsresult rv = context->InitContext(); |
michael@0 | 1913 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1914 | |
michael@0 | 1915 | outer->mContext = context; |
michael@0 | 1916 | return NS_OK; |
michael@0 | 1917 | } |
michael@0 | 1918 | |
michael@0 | 1919 | nsIScriptContext * |
michael@0 | 1920 | nsGlobalWindow::GetScriptContext() |
michael@0 | 1921 | { |
michael@0 | 1922 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 1923 | return outer ? outer->mContext : nullptr; |
michael@0 | 1924 | } |
michael@0 | 1925 | |
michael@0 | 1926 | JSObject * |
michael@0 | 1927 | nsGlobalWindow::GetGlobalJSObject() |
michael@0 | 1928 | { |
michael@0 | 1929 | return FastGetGlobalJSObject(); |
michael@0 | 1930 | } |
michael@0 | 1931 | |
michael@0 | 1932 | void |
michael@0 | 1933 | nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc) |
michael@0 | 1934 | { |
michael@0 | 1935 | TraceWrapper(aTrc, "active window global"); |
michael@0 | 1936 | } |
michael@0 | 1937 | |
michael@0 | 1938 | /* static */ |
michael@0 | 1939 | JSObject* |
michael@0 | 1940 | nsGlobalWindow::OuterObject(JSContext* aCx, JS::Handle<JSObject*> aObj) |
michael@0 | 1941 | { |
michael@0 | 1942 | nsGlobalWindow* origWin; |
michael@0 | 1943 | UNWRAP_OBJECT(Window, aObj, origWin); |
michael@0 | 1944 | nsGlobalWindow* win = origWin->GetOuterWindowInternal(); |
michael@0 | 1945 | |
michael@0 | 1946 | if (!win) { |
michael@0 | 1947 | // If we no longer have an outer window. No code should ever be |
michael@0 | 1948 | // running on a window w/o an outer, which means this hook should |
michael@0 | 1949 | // never be called when we have no outer. But just in case, return |
michael@0 | 1950 | // null to prevent leaking an inner window to code in a different |
michael@0 | 1951 | // window. |
michael@0 | 1952 | NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); |
michael@0 | 1953 | return nullptr; |
michael@0 | 1954 | } |
michael@0 | 1955 | |
michael@0 | 1956 | JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject()); |
michael@0 | 1957 | MOZ_ASSERT(winObj); |
michael@0 | 1958 | |
michael@0 | 1959 | // Note that while |wrapper| is same-compartment with cx, the outer window |
michael@0 | 1960 | // might not be. If we're running script in an inactive scope and evalute |
michael@0 | 1961 | // |this|, the outer window is actually a cross-compartment wrapper. So we |
michael@0 | 1962 | // need to wrap here. |
michael@0 | 1963 | if (!JS_WrapObject(aCx, &winObj)) { |
michael@0 | 1964 | NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); |
michael@0 | 1965 | return nullptr; |
michael@0 | 1966 | } |
michael@0 | 1967 | |
michael@0 | 1968 | return winObj; |
michael@0 | 1969 | } |
michael@0 | 1970 | |
michael@0 | 1971 | bool |
michael@0 | 1972 | nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument) |
michael@0 | 1973 | { |
michael@0 | 1974 | // We reuse the inner window when: |
michael@0 | 1975 | // a. We are currently at our original document. |
michael@0 | 1976 | // b. At least one of the following conditions are true: |
michael@0 | 1977 | // -- The new document is the same as the old document. This means that we're |
michael@0 | 1978 | // getting called from document.open(). |
michael@0 | 1979 | // -- The new document has the same origin as what we have loaded right now. |
michael@0 | 1980 | |
michael@0 | 1981 | if (!mDoc || !aNewDocument) { |
michael@0 | 1982 | return false; |
michael@0 | 1983 | } |
michael@0 | 1984 | |
michael@0 | 1985 | if (!mDoc->IsInitialDocument()) { |
michael@0 | 1986 | return false; |
michael@0 | 1987 | } |
michael@0 | 1988 | |
michael@0 | 1989 | NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()), |
michael@0 | 1990 | "How'd this happen?"); |
michael@0 | 1991 | |
michael@0 | 1992 | // Great, we're the original document, check for one of the other |
michael@0 | 1993 | // conditions. |
michael@0 | 1994 | |
michael@0 | 1995 | if (mDoc == aNewDocument) { |
michael@0 | 1996 | return true; |
michael@0 | 1997 | } |
michael@0 | 1998 | |
michael@0 | 1999 | bool equal; |
michael@0 | 2000 | if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), |
michael@0 | 2001 | &equal)) && |
michael@0 | 2002 | equal) { |
michael@0 | 2003 | // The origin is the same. |
michael@0 | 2004 | return true; |
michael@0 | 2005 | } |
michael@0 | 2006 | |
michael@0 | 2007 | return false; |
michael@0 | 2008 | } |
michael@0 | 2009 | |
michael@0 | 2010 | void |
michael@0 | 2011 | nsGlobalWindow::SetInitialPrincipalToSubject() |
michael@0 | 2012 | { |
michael@0 | 2013 | FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ()); |
michael@0 | 2014 | |
michael@0 | 2015 | // First, grab the subject principal. |
michael@0 | 2016 | nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::GetSubjectPrincipal(); |
michael@0 | 2017 | if (!newWindowPrincipal) { |
michael@0 | 2018 | newWindowPrincipal = nsContentUtils::GetSystemPrincipal(); |
michael@0 | 2019 | } |
michael@0 | 2020 | |
michael@0 | 2021 | // Now, if we're about to use the system principal or an nsExpandedPrincipal, |
michael@0 | 2022 | // make sure we're not using it for a content docshell. |
michael@0 | 2023 | if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) && |
michael@0 | 2024 | GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) { |
michael@0 | 2025 | newWindowPrincipal = nullptr; |
michael@0 | 2026 | } |
michael@0 | 2027 | |
michael@0 | 2028 | // If there's an existing document, bail if it either: |
michael@0 | 2029 | if (mDoc) { |
michael@0 | 2030 | // (a) is not an initial about:blank document, or |
michael@0 | 2031 | if (!mDoc->IsInitialDocument()) |
michael@0 | 2032 | return; |
michael@0 | 2033 | // (b) already has the correct principal. |
michael@0 | 2034 | if (mDoc->NodePrincipal() == newWindowPrincipal) |
michael@0 | 2035 | return; |
michael@0 | 2036 | |
michael@0 | 2037 | #ifdef DEBUG |
michael@0 | 2038 | // If we have a document loaded at this point, it had better be about:blank. |
michael@0 | 2039 | // Otherwise, something is really weird. |
michael@0 | 2040 | nsCOMPtr<nsIURI> uri; |
michael@0 | 2041 | mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri)); |
michael@0 | 2042 | NS_ASSERTION(uri && NS_IsAboutBlank(uri) && |
michael@0 | 2043 | NS_IsAboutBlank(mDoc->GetDocumentURI()), |
michael@0 | 2044 | "Unexpected original document"); |
michael@0 | 2045 | #endif |
michael@0 | 2046 | } |
michael@0 | 2047 | |
michael@0 | 2048 | GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal); |
michael@0 | 2049 | mDoc->SetIsInitialDocument(true); |
michael@0 | 2050 | |
michael@0 | 2051 | nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell(); |
michael@0 | 2052 | |
michael@0 | 2053 | if (shell && !shell->DidInitialize()) { |
michael@0 | 2054 | // Ensure that if someone plays with this document they will get |
michael@0 | 2055 | // layout happening. |
michael@0 | 2056 | nsRect r = shell->GetPresContext()->GetVisibleArea(); |
michael@0 | 2057 | shell->Initialize(r.width, r.height); |
michael@0 | 2058 | } |
michael@0 | 2059 | } |
michael@0 | 2060 | |
michael@0 | 2061 | PopupControlState |
michael@0 | 2062 | PushPopupControlState(PopupControlState aState, bool aForce) |
michael@0 | 2063 | { |
michael@0 | 2064 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 2065 | |
michael@0 | 2066 | PopupControlState oldState = gPopupControlState; |
michael@0 | 2067 | |
michael@0 | 2068 | if (aState < gPopupControlState || aForce) { |
michael@0 | 2069 | gPopupControlState = aState; |
michael@0 | 2070 | } |
michael@0 | 2071 | |
michael@0 | 2072 | return oldState; |
michael@0 | 2073 | } |
michael@0 | 2074 | |
michael@0 | 2075 | void |
michael@0 | 2076 | PopPopupControlState(PopupControlState aState) |
michael@0 | 2077 | { |
michael@0 | 2078 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 2079 | |
michael@0 | 2080 | gPopupControlState = aState; |
michael@0 | 2081 | } |
michael@0 | 2082 | |
michael@0 | 2083 | PopupControlState |
michael@0 | 2084 | nsGlobalWindow::PushPopupControlState(PopupControlState aState, |
michael@0 | 2085 | bool aForce) const |
michael@0 | 2086 | { |
michael@0 | 2087 | return ::PushPopupControlState(aState, aForce); |
michael@0 | 2088 | } |
michael@0 | 2089 | |
michael@0 | 2090 | void |
michael@0 | 2091 | nsGlobalWindow::PopPopupControlState(PopupControlState aState) const |
michael@0 | 2092 | { |
michael@0 | 2093 | ::PopPopupControlState(aState); |
michael@0 | 2094 | } |
michael@0 | 2095 | |
michael@0 | 2096 | PopupControlState |
michael@0 | 2097 | nsGlobalWindow::GetPopupControlState() const |
michael@0 | 2098 | { |
michael@0 | 2099 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 2100 | return gPopupControlState; |
michael@0 | 2101 | } |
michael@0 | 2102 | |
michael@0 | 2103 | #define WINDOWSTATEHOLDER_IID \ |
michael@0 | 2104 | {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}} |
michael@0 | 2105 | |
michael@0 | 2106 | class WindowStateHolder MOZ_FINAL : public nsISupports |
michael@0 | 2107 | { |
michael@0 | 2108 | public: |
michael@0 | 2109 | NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) |
michael@0 | 2110 | NS_DECL_ISUPPORTS |
michael@0 | 2111 | |
michael@0 | 2112 | WindowStateHolder(nsGlobalWindow *aWindow); |
michael@0 | 2113 | |
michael@0 | 2114 | nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } |
michael@0 | 2115 | |
michael@0 | 2116 | void DidRestoreWindow() |
michael@0 | 2117 | { |
michael@0 | 2118 | mInnerWindow = nullptr; |
michael@0 | 2119 | } |
michael@0 | 2120 | |
michael@0 | 2121 | protected: |
michael@0 | 2122 | ~WindowStateHolder(); |
michael@0 | 2123 | |
michael@0 | 2124 | nsRefPtr<nsGlobalWindow> mInnerWindow; |
michael@0 | 2125 | }; |
michael@0 | 2126 | |
michael@0 | 2127 | NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) |
michael@0 | 2128 | |
michael@0 | 2129 | WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow) |
michael@0 | 2130 | : mInnerWindow(aWindow) |
michael@0 | 2131 | { |
michael@0 | 2132 | NS_PRECONDITION(aWindow, "null window"); |
michael@0 | 2133 | NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); |
michael@0 | 2134 | |
michael@0 | 2135 | // We hold onto this to make sure the inner window doesn't go away. The outer |
michael@0 | 2136 | // window ends up recalculating it anyway. |
michael@0 | 2137 | mInnerWindow->PreserveWrapper(ToSupports(mInnerWindow)); |
michael@0 | 2138 | |
michael@0 | 2139 | aWindow->SuspendTimeouts(); |
michael@0 | 2140 | |
michael@0 | 2141 | // When a global goes into the bfcache, we disable script. |
michael@0 | 2142 | xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false); |
michael@0 | 2143 | } |
michael@0 | 2144 | |
michael@0 | 2145 | WindowStateHolder::~WindowStateHolder() |
michael@0 | 2146 | { |
michael@0 | 2147 | if (mInnerWindow) { |
michael@0 | 2148 | // This window was left in the bfcache and is now going away. We need to |
michael@0 | 2149 | // free it up. |
michael@0 | 2150 | // Note that FreeInnerObjects may already have been called on the |
michael@0 | 2151 | // inner window if its outer has already had SetDocShell(null) |
michael@0 | 2152 | // called. |
michael@0 | 2153 | mInnerWindow->FreeInnerObjects(); |
michael@0 | 2154 | } |
michael@0 | 2155 | } |
michael@0 | 2156 | |
michael@0 | 2157 | NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder) |
michael@0 | 2158 | |
michael@0 | 2159 | // We need certain special behavior for remote XUL whitelisted domains, but we |
michael@0 | 2160 | // don't want that behavior to take effect in automation, because we whitelist |
michael@0 | 2161 | // all the mochitest domains. So we need to check a pref here. |
michael@0 | 2162 | static bool |
michael@0 | 2163 | TreatAsRemoteXUL(nsIPrincipal* aPrincipal) |
michael@0 | 2164 | { |
michael@0 | 2165 | MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(aPrincipal)); |
michael@0 | 2166 | return nsContentUtils::AllowXULXBLForPrincipal(aPrincipal) && |
michael@0 | 2167 | !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false); |
michael@0 | 2168 | } |
michael@0 | 2169 | |
michael@0 | 2170 | /** |
michael@0 | 2171 | * Create a new global object that will be used for an inner window. |
michael@0 | 2172 | * Return the native global and an nsISupports 'holder' that can be used |
michael@0 | 2173 | * to manage the lifetime of it. |
michael@0 | 2174 | */ |
michael@0 | 2175 | static nsresult |
michael@0 | 2176 | CreateNativeGlobalForInner(JSContext* aCx, |
michael@0 | 2177 | nsGlobalWindow* aNewInner, |
michael@0 | 2178 | nsIURI* aURI, |
michael@0 | 2179 | nsIPrincipal* aPrincipal, |
michael@0 | 2180 | JS::MutableHandle<JSObject*> aGlobal) |
michael@0 | 2181 | { |
michael@0 | 2182 | MOZ_ASSERT(aCx); |
michael@0 | 2183 | MOZ_ASSERT(aNewInner); |
michael@0 | 2184 | MOZ_ASSERT(aNewInner->IsInnerWindow()); |
michael@0 | 2185 | MOZ_ASSERT(aPrincipal); |
michael@0 | 2186 | |
michael@0 | 2187 | // DOMWindow with nsEP is not supported, we have to make sure |
michael@0 | 2188 | // no one creates one accidentally. |
michael@0 | 2189 | nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal); |
michael@0 | 2190 | MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported"); |
michael@0 | 2191 | |
michael@0 | 2192 | nsGlobalWindow *top = nullptr; |
michael@0 | 2193 | if (aNewInner->GetOuterWindow()) { |
michael@0 | 2194 | top = aNewInner->GetTop(); |
michael@0 | 2195 | } |
michael@0 | 2196 | JS::CompartmentOptions options; |
michael@0 | 2197 | if (top) { |
michael@0 | 2198 | if (top->GetGlobalJSObject()) { |
michael@0 | 2199 | options.setSameZoneAs(top->GetGlobalJSObject()); |
michael@0 | 2200 | } |
michael@0 | 2201 | } |
michael@0 | 2202 | |
michael@0 | 2203 | nsIXPConnect* xpc = nsContentUtils::XPConnect(); |
michael@0 | 2204 | |
michael@0 | 2205 | // Determine if we need the Components object. |
michael@0 | 2206 | bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) || |
michael@0 | 2207 | TreatAsRemoteXUL(aPrincipal); |
michael@0 | 2208 | uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT; |
michael@0 | 2209 | flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK; |
michael@0 | 2210 | |
michael@0 | 2211 | nsCOMPtr<nsIXPConnectJSObjectHolder> holder; |
michael@0 | 2212 | nsresult rv = xpc->InitClassesWithNewWrappedGlobal( |
michael@0 | 2213 | aCx, ToSupports(aNewInner), |
michael@0 | 2214 | aPrincipal, flags, options, getter_AddRefs(holder)); |
michael@0 | 2215 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2216 | |
michael@0 | 2217 | aGlobal.set(holder->GetJSObject()); |
michael@0 | 2218 | |
michael@0 | 2219 | // Set the location information for the new global, so that tools like |
michael@0 | 2220 | // about:memory may use that information |
michael@0 | 2221 | MOZ_ASSERT(aGlobal); |
michael@0 | 2222 | MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal); |
michael@0 | 2223 | xpc::SetLocationForGlobal(aGlobal, aURI); |
michael@0 | 2224 | |
michael@0 | 2225 | return NS_OK; |
michael@0 | 2226 | } |
michael@0 | 2227 | |
michael@0 | 2228 | nsresult |
michael@0 | 2229 | nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, |
michael@0 | 2230 | nsISupports* aState, |
michael@0 | 2231 | bool aForceReuseInnerWindow) |
michael@0 | 2232 | { |
michael@0 | 2233 | NS_PRECONDITION(mDocumentPrincipal == nullptr, |
michael@0 | 2234 | "mDocumentPrincipal prematurely set!"); |
michael@0 | 2235 | MOZ_ASSERT(aDocument); |
michael@0 | 2236 | |
michael@0 | 2237 | if (IsInnerWindow()) { |
michael@0 | 2238 | if (!mOuterWindow) { |
michael@0 | 2239 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 2240 | } |
michael@0 | 2241 | |
michael@0 | 2242 | // Refuse to set a new document if the call came from an inner |
michael@0 | 2243 | // window that's not the current inner window. |
michael@0 | 2244 | if (mOuterWindow->GetCurrentInnerWindow() != this) { |
michael@0 | 2245 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 2246 | } |
michael@0 | 2247 | |
michael@0 | 2248 | return GetOuterWindowInternal()->SetNewDocument(aDocument, aState, |
michael@0 | 2249 | aForceReuseInnerWindow); |
michael@0 | 2250 | } |
michael@0 | 2251 | |
michael@0 | 2252 | NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows"); |
michael@0 | 2253 | |
michael@0 | 2254 | if (IsFrozen()) { |
michael@0 | 2255 | // This outer is now getting its first inner, thaw the outer now |
michael@0 | 2256 | // that it's ready and is getting an inner window. |
michael@0 | 2257 | |
michael@0 | 2258 | Thaw(); |
michael@0 | 2259 | } |
michael@0 | 2260 | |
michael@0 | 2261 | NS_ASSERTION(!GetCurrentInnerWindow() || |
michael@0 | 2262 | GetCurrentInnerWindow()->GetExtantDoc() == mDoc, |
michael@0 | 2263 | "Uh, mDoc doesn't match the current inner window " |
michael@0 | 2264 | "document!"); |
michael@0 | 2265 | |
michael@0 | 2266 | bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument); |
michael@0 | 2267 | if (aForceReuseInnerWindow && |
michael@0 | 2268 | !wouldReuseInnerWindow && |
michael@0 | 2269 | mDoc && |
michael@0 | 2270 | mDoc->NodePrincipal() != aDocument->NodePrincipal()) { |
michael@0 | 2271 | NS_ERROR("Attempted forced inner window reuse while changing principal"); |
michael@0 | 2272 | return NS_ERROR_UNEXPECTED; |
michael@0 | 2273 | } |
michael@0 | 2274 | |
michael@0 | 2275 | nsCOMPtr<nsIDocument> oldDoc = mDoc; |
michael@0 | 2276 | |
michael@0 | 2277 | nsIScriptContext *scx = GetContextInternal(); |
michael@0 | 2278 | NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 2279 | |
michael@0 | 2280 | JSContext *cx = scx->GetNativeContext(); |
michael@0 | 2281 | |
michael@0 | 2282 | #ifndef MOZ_DISABLE_CRYPTOLEGACY |
michael@0 | 2283 | // clear smartcard events, our document has gone away. |
michael@0 | 2284 | if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) { |
michael@0 | 2285 | nsresult rv = mCrypto->SetEnableSmartCardEvents(false); |
michael@0 | 2286 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2287 | } |
michael@0 | 2288 | #endif |
michael@0 | 2289 | |
michael@0 | 2290 | if (!mDoc) { |
michael@0 | 2291 | // First document load. |
michael@0 | 2292 | |
michael@0 | 2293 | // Get our private root. If it is equal to us, then we need to |
michael@0 | 2294 | // attach our global key bindings that handles browser scrolling |
michael@0 | 2295 | // and other browser commands. |
michael@0 | 2296 | nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot(); |
michael@0 | 2297 | |
michael@0 | 2298 | if (privateRoot == static_cast<nsIDOMWindow*>(this)) { |
michael@0 | 2299 | nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler); |
michael@0 | 2300 | } |
michael@0 | 2301 | } |
michael@0 | 2302 | |
michael@0 | 2303 | /* No mDocShell means we're already been partially closed down. When that |
michael@0 | 2304 | happens, setting status isn't a big requirement, so don't. (Doesn't happen |
michael@0 | 2305 | under normal circumstances, but bug 49615 describes a case.) */ |
michael@0 | 2306 | |
michael@0 | 2307 | nsContentUtils::AddScriptRunner( |
michael@0 | 2308 | NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); |
michael@0 | 2309 | |
michael@0 | 2310 | // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner |
michael@0 | 2311 | // window (see bug 776497). Be safe. |
michael@0 | 2312 | bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) && |
michael@0 | 2313 | GetCurrentInnerWindowInternal(); |
michael@0 | 2314 | |
michael@0 | 2315 | nsresult rv = NS_OK; |
michael@0 | 2316 | |
michael@0 | 2317 | // Set mDoc even if this is an outer window to avoid |
michael@0 | 2318 | // having to *always* reach into the inner window to find the |
michael@0 | 2319 | // document. |
michael@0 | 2320 | mDoc = aDocument; |
michael@0 | 2321 | if (IsInnerWindow() && IsDOMBinding()) { |
michael@0 | 2322 | WindowBinding::ClearCachedDocumentValue(cx, this); |
michael@0 | 2323 | } |
michael@0 | 2324 | |
michael@0 | 2325 | // Take this opportunity to clear mSuspendedDoc. Our old inner window is now |
michael@0 | 2326 | // responsible for unsuspending it. |
michael@0 | 2327 | mSuspendedDoc = nullptr; |
michael@0 | 2328 | |
michael@0 | 2329 | #ifdef DEBUG |
michael@0 | 2330 | mLastOpenedURI = aDocument->GetDocumentURI(); |
michael@0 | 2331 | #endif |
michael@0 | 2332 | |
michael@0 | 2333 | mContext->WillInitializeContext(); |
michael@0 | 2334 | |
michael@0 | 2335 | nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); |
michael@0 | 2336 | |
michael@0 | 2337 | if (currentInner && currentInner->mNavigator) { |
michael@0 | 2338 | currentInner->mNavigator->OnNavigation(); |
michael@0 | 2339 | } |
michael@0 | 2340 | |
michael@0 | 2341 | nsRefPtr<nsGlobalWindow> newInnerWindow; |
michael@0 | 2342 | bool createdInnerWindow = false; |
michael@0 | 2343 | |
michael@0 | 2344 | bool thisChrome = IsChromeWindow(); |
michael@0 | 2345 | |
michael@0 | 2346 | nsCxPusher cxPusher; |
michael@0 | 2347 | cxPusher.Push(cx); |
michael@0 | 2348 | |
michael@0 | 2349 | // Check if we're near the stack limit before we get anywhere near the |
michael@0 | 2350 | // transplanting code. |
michael@0 | 2351 | JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE); |
michael@0 | 2352 | |
michael@0 | 2353 | nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState); |
michael@0 | 2354 | NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); |
michael@0 | 2355 | |
michael@0 | 2356 | JS::Rooted<JSObject*> newInnerGlobal(cx); |
michael@0 | 2357 | if (reUseInnerWindow) { |
michael@0 | 2358 | // We're reusing the current inner window. |
michael@0 | 2359 | NS_ASSERTION(!currentInner->IsFrozen(), |
michael@0 | 2360 | "We should never be reusing a shared inner window"); |
michael@0 | 2361 | newInnerWindow = currentInner; |
michael@0 | 2362 | newInnerGlobal = currentInner->GetWrapperPreserveColor(); |
michael@0 | 2363 | |
michael@0 | 2364 | if (aDocument != oldDoc) { |
michael@0 | 2365 | JS::ExposeObjectToActiveJS(newInnerGlobal); |
michael@0 | 2366 | } |
michael@0 | 2367 | |
michael@0 | 2368 | // We're reusing the inner window, but this still counts as a navigation, |
michael@0 | 2369 | // so all expandos and such defined on the outer window should go away. Force |
michael@0 | 2370 | // all Xray wrappers to be recomputed. |
michael@0 | 2371 | JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor()); |
michael@0 | 2372 | JS::ExposeObjectToActiveJS(rootedObject); |
michael@0 | 2373 | if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) { |
michael@0 | 2374 | return NS_ERROR_FAILURE; |
michael@0 | 2375 | } |
michael@0 | 2376 | |
michael@0 | 2377 | // Inner windows are only reused for same-origin principals, but the principals |
michael@0 | 2378 | // don't necessarily match exactly. Update the principal on the compartment to |
michael@0 | 2379 | // match the new document. |
michael@0 | 2380 | // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here |
michael@0 | 2381 | // because we haven't yet set its mDoc to aDocument. |
michael@0 | 2382 | JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal); |
michael@0 | 2383 | #ifdef DEBUG |
michael@0 | 2384 | bool sameOrigin = false; |
michael@0 | 2385 | nsIPrincipal *existing = |
michael@0 | 2386 | nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment)); |
michael@0 | 2387 | aDocument->NodePrincipal()->Equals(existing, &sameOrigin); |
michael@0 | 2388 | MOZ_ASSERT(sameOrigin); |
michael@0 | 2389 | #endif |
michael@0 | 2390 | JS_SetCompartmentPrincipals(compartment, |
michael@0 | 2391 | nsJSPrincipals::get(aDocument->NodePrincipal())); |
michael@0 | 2392 | } else { |
michael@0 | 2393 | if (aState) { |
michael@0 | 2394 | newInnerWindow = wsh->GetInnerWindow(); |
michael@0 | 2395 | newInnerGlobal = newInnerWindow->GetWrapperPreserveColor(); |
michael@0 | 2396 | } else { |
michael@0 | 2397 | if (thisChrome) { |
michael@0 | 2398 | newInnerWindow = new nsGlobalChromeWindow(this); |
michael@0 | 2399 | } else if (mIsModalContentWindow) { |
michael@0 | 2400 | newInnerWindow = new nsGlobalModalWindow(this); |
michael@0 | 2401 | } else { |
michael@0 | 2402 | newInnerWindow = new nsGlobalWindow(this); |
michael@0 | 2403 | } |
michael@0 | 2404 | |
michael@0 | 2405 | // Freeze the outer window and null out the inner window so |
michael@0 | 2406 | // that initializing classes on the new inner doesn't end up |
michael@0 | 2407 | // reaching into the old inner window for classes etc. |
michael@0 | 2408 | // |
michael@0 | 2409 | // [This happens with Object.prototype when XPConnect creates |
michael@0 | 2410 | // a temporary global while initializing classes; the reason |
michael@0 | 2411 | // being that xpconnect creates the temp global w/o a parent |
michael@0 | 2412 | // and proto, which makes the JS engine look up classes in |
michael@0 | 2413 | // cx->globalObject, i.e. this outer window]. |
michael@0 | 2414 | |
michael@0 | 2415 | mInnerWindow = nullptr; |
michael@0 | 2416 | |
michael@0 | 2417 | Freeze(); |
michael@0 | 2418 | mCreatingInnerWindow = true; |
michael@0 | 2419 | // Every script context we are initialized with must create a |
michael@0 | 2420 | // new global. |
michael@0 | 2421 | rv = CreateNativeGlobalForInner(cx, newInnerWindow, |
michael@0 | 2422 | aDocument->GetDocumentURI(), |
michael@0 | 2423 | aDocument->NodePrincipal(), |
michael@0 | 2424 | &newInnerGlobal); |
michael@0 | 2425 | NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal && |
michael@0 | 2426 | newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal, |
michael@0 | 2427 | "Failed to get script global"); |
michael@0 | 2428 | |
michael@0 | 2429 | mCreatingInnerWindow = false; |
michael@0 | 2430 | createdInnerWindow = true; |
michael@0 | 2431 | Thaw(); |
michael@0 | 2432 | |
michael@0 | 2433 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2434 | } |
michael@0 | 2435 | |
michael@0 | 2436 | if (currentInner && currentInner->GetWrapperPreserveColor()) { |
michael@0 | 2437 | if (oldDoc == aDocument) { |
michael@0 | 2438 | // Move the navigator from the old inner window to the new one since |
michael@0 | 2439 | // this is a document.write. This is safe from a same-origin point of |
michael@0 | 2440 | // view because document.write can only be used by the same origin. |
michael@0 | 2441 | newInnerWindow->mNavigator = currentInner->mNavigator; |
michael@0 | 2442 | currentInner->mNavigator = nullptr; |
michael@0 | 2443 | if (newInnerWindow->mNavigator) { |
michael@0 | 2444 | newInnerWindow->mNavigator->SetWindow(newInnerWindow); |
michael@0 | 2445 | } |
michael@0 | 2446 | |
michael@0 | 2447 | // Make a copy of the old window's performance object on document.open. |
michael@0 | 2448 | // Note that we have to force eager creation of it here, because we need |
michael@0 | 2449 | // to grab the current document channel and whatnot before that changes. |
michael@0 | 2450 | currentInner->CreatePerformanceObjectIfNeeded(); |
michael@0 | 2451 | if (currentInner->mPerformance) { |
michael@0 | 2452 | newInnerWindow->mPerformance = |
michael@0 | 2453 | new nsPerformance(newInnerWindow, |
michael@0 | 2454 | currentInner->mPerformance->GetDOMTiming(), |
michael@0 | 2455 | currentInner->mPerformance->GetChannel(), |
michael@0 | 2456 | currentInner->mPerformance->GetParentPerformance()); |
michael@0 | 2457 | } |
michael@0 | 2458 | } |
michael@0 | 2459 | |
michael@0 | 2460 | // Don't free objects on our current inner window if it's going to be |
michael@0 | 2461 | // held in the bfcache. |
michael@0 | 2462 | if (!currentInner->IsFrozen()) { |
michael@0 | 2463 | currentInner->FreeInnerObjects(); |
michael@0 | 2464 | } |
michael@0 | 2465 | } |
michael@0 | 2466 | |
michael@0 | 2467 | mInnerWindow = newInnerWindow; |
michael@0 | 2468 | |
michael@0 | 2469 | if (!GetWrapperPreserveColor()) { |
michael@0 | 2470 | JS::Rooted<JSObject*> outer(cx, |
michael@0 | 2471 | NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); |
michael@0 | 2472 | NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE); |
michael@0 | 2473 | |
michael@0 | 2474 | js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this))); |
michael@0 | 2475 | |
michael@0 | 2476 | // Inform the nsJSContext, which is the canonical holder of the outer. |
michael@0 | 2477 | mContext->SetWindowProxy(outer); |
michael@0 | 2478 | mContext->DidInitializeContext(); |
michael@0 | 2479 | |
michael@0 | 2480 | SetWrapper(mContext->GetWindowProxy()); |
michael@0 | 2481 | } else { |
michael@0 | 2482 | JS::ExposeObjectToActiveJS(newInnerGlobal); |
michael@0 | 2483 | JS::Rooted<JSObject*> outerObject(cx, |
michael@0 | 2484 | NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); |
michael@0 | 2485 | if (!outerObject) { |
michael@0 | 2486 | NS_ERROR("out of memory"); |
michael@0 | 2487 | return NS_ERROR_FAILURE; |
michael@0 | 2488 | } |
michael@0 | 2489 | |
michael@0 | 2490 | JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor()); |
michael@0 | 2491 | |
michael@0 | 2492 | js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr)); |
michael@0 | 2493 | |
michael@0 | 2494 | outerObject = xpc::TransplantObject(cx, obj, outerObject); |
michael@0 | 2495 | if (!outerObject) { |
michael@0 | 2496 | NS_ERROR("unable to transplant wrappers, probably OOM"); |
michael@0 | 2497 | return NS_ERROR_FAILURE; |
michael@0 | 2498 | } |
michael@0 | 2499 | |
michael@0 | 2500 | js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this))); |
michael@0 | 2501 | |
michael@0 | 2502 | SetWrapper(outerObject); |
michael@0 | 2503 | |
michael@0 | 2504 | { |
michael@0 | 2505 | JSAutoCompartment ac(cx, outerObject); |
michael@0 | 2506 | |
michael@0 | 2507 | JS_SetParent(cx, outerObject, newInnerGlobal); |
michael@0 | 2508 | |
michael@0 | 2509 | // Inform the nsJSContext, which is the canonical holder of the outer. |
michael@0 | 2510 | mContext->SetWindowProxy(outerObject); |
michael@0 | 2511 | |
michael@0 | 2512 | NS_ASSERTION(!JS_IsExceptionPending(cx), |
michael@0 | 2513 | "We might overwrite a pending exception!"); |
michael@0 | 2514 | XPCWrappedNativeScope* scope = xpc::GetObjectScope(outerObject); |
michael@0 | 2515 | if (scope->mWaiverWrapperMap) { |
michael@0 | 2516 | scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal); |
michael@0 | 2517 | } |
michael@0 | 2518 | } |
michael@0 | 2519 | } |
michael@0 | 2520 | |
michael@0 | 2521 | // Enter the new global's compartment. |
michael@0 | 2522 | JSAutoCompartment ac(cx, GetWrapperPreserveColor()); |
michael@0 | 2523 | |
michael@0 | 2524 | // Set scriptability based on the state of the docshell. |
michael@0 | 2525 | bool allow = GetDocShell()->GetCanExecuteScripts(); |
michael@0 | 2526 | xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow); |
michael@0 | 2527 | |
michael@0 | 2528 | // If we created a new inner window above, we need to do the last little bit |
michael@0 | 2529 | // of initialization now that the dust has settled. |
michael@0 | 2530 | if (createdInnerWindow) { |
michael@0 | 2531 | nsIXPConnect *xpc = nsContentUtils::XPConnect(); |
michael@0 | 2532 | nsCOMPtr<nsIXPConnectWrappedNative> wrapper; |
michael@0 | 2533 | nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal, |
michael@0 | 2534 | getter_AddRefs(wrapper)); |
michael@0 | 2535 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2536 | NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); |
michael@0 | 2537 | rv = wrapper->FinishInitForWrappedGlobal(); |
michael@0 | 2538 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2539 | } |
michael@0 | 2540 | |
michael@0 | 2541 | if (!aState) { |
michael@0 | 2542 | JS::Rooted<JSObject*> rootedWrapper(cx, GetWrapperPreserveColor()); |
michael@0 | 2543 | if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper, |
michael@0 | 2544 | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, |
michael@0 | 2545 | JS_PropertyStub, JS_StrictPropertyStub)) { |
michael@0 | 2546 | NS_ERROR("can't create the 'window' property"); |
michael@0 | 2547 | return NS_ERROR_FAILURE; |
michael@0 | 2548 | } |
michael@0 | 2549 | } |
michael@0 | 2550 | } |
michael@0 | 2551 | |
michael@0 | 2552 | JSAutoCompartment ac(cx, GetWrapperPreserveColor()); |
michael@0 | 2553 | |
michael@0 | 2554 | if (!aState && !reUseInnerWindow) { |
michael@0 | 2555 | // Loading a new page and creating a new inner window, *not* |
michael@0 | 2556 | // restoring from session history. |
michael@0 | 2557 | |
michael@0 | 2558 | // Now that both the the inner and outer windows are initialized |
michael@0 | 2559 | // let the script context do its magic to hook them together. |
michael@0 | 2560 | MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor()); |
michael@0 | 2561 | #ifdef DEBUG |
michael@0 | 2562 | JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor()); |
michael@0 | 2563 | JS::Rooted<JSObject*> proto1(cx), proto2(cx); |
michael@0 | 2564 | JS_GetPrototype(cx, rootedJSObject, &proto1); |
michael@0 | 2565 | JS_GetPrototype(cx, newInnerGlobal, &proto2); |
michael@0 | 2566 | NS_ASSERTION(proto1 == proto2, |
michael@0 | 2567 | "outer and inner globals should have the same prototype"); |
michael@0 | 2568 | #endif |
michael@0 | 2569 | |
michael@0 | 2570 | nsCOMPtr<Element> frame = GetFrameElementInternal(); |
michael@0 | 2571 | if (frame) { |
michael@0 | 2572 | nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow(); |
michael@0 | 2573 | if (parentWindow && parentWindow->TimeoutSuspendCount()) { |
michael@0 | 2574 | SuspendTimeouts(parentWindow->TimeoutSuspendCount()); |
michael@0 | 2575 | } |
michael@0 | 2576 | } |
michael@0 | 2577 | } |
michael@0 | 2578 | |
michael@0 | 2579 | // Add an extra ref in case we release mContext during GC. |
michael@0 | 2580 | nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext); |
michael@0 | 2581 | |
michael@0 | 2582 | aDocument->SetScriptGlobalObject(newInnerWindow); |
michael@0 | 2583 | |
michael@0 | 2584 | if (!aState) { |
michael@0 | 2585 | if (reUseInnerWindow) { |
michael@0 | 2586 | if (newInnerWindow->mDoc != aDocument) { |
michael@0 | 2587 | newInnerWindow->mDoc = aDocument; |
michael@0 | 2588 | |
michael@0 | 2589 | if (newInnerWindow->IsDOMBinding()) { |
michael@0 | 2590 | WindowBinding::ClearCachedDocumentValue(cx, newInnerWindow); |
michael@0 | 2591 | } else { |
michael@0 | 2592 | // We're reusing the inner window for a new document. In this |
michael@0 | 2593 | // case we don't clear the inner window's scope, but we must |
michael@0 | 2594 | // make sure the cached document property gets updated. |
michael@0 | 2595 | |
michael@0 | 2596 | JS::Rooted<JSObject*> obj(cx, |
michael@0 | 2597 | currentInner->GetWrapperPreserveColor()); |
michael@0 | 2598 | ::JS_DeleteProperty(cx, obj, "document"); |
michael@0 | 2599 | } |
michael@0 | 2600 | } |
michael@0 | 2601 | } else { |
michael@0 | 2602 | newInnerWindow->InnerSetNewDocument(cx, aDocument); |
michael@0 | 2603 | |
michael@0 | 2604 | // Initialize DOM classes etc on the inner window. |
michael@0 | 2605 | JS::Rooted<JSObject*> obj(cx, newInnerGlobal); |
michael@0 | 2606 | rv = mContext->InitClasses(obj); |
michael@0 | 2607 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 2608 | } |
michael@0 | 2609 | |
michael@0 | 2610 | // If the document comes from a JAR, check if the channel was determined |
michael@0 | 2611 | // to be unsafe. If so, permanently disable script on the compartment by |
michael@0 | 2612 | // calling Block() and throwing away the key. |
michael@0 | 2613 | nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel()); |
michael@0 | 2614 | if (jarChannel && jarChannel->GetIsUnsafe()) { |
michael@0 | 2615 | xpc::Scriptability::Get(newInnerGlobal).Block(); |
michael@0 | 2616 | } |
michael@0 | 2617 | |
michael@0 | 2618 | if (mArguments) { |
michael@0 | 2619 | newInnerWindow->DefineArgumentsProperty(mArguments); |
michael@0 | 2620 | mArguments = nullptr; |
michael@0 | 2621 | } |
michael@0 | 2622 | |
michael@0 | 2623 | // Give the new inner window our chrome event handler (since it |
michael@0 | 2624 | // doesn't have one). |
michael@0 | 2625 | newInnerWindow->mChromeEventHandler = mChromeEventHandler; |
michael@0 | 2626 | } |
michael@0 | 2627 | |
michael@0 | 2628 | mContext->GC(JS::gcreason::SET_NEW_DOCUMENT); |
michael@0 | 2629 | mContext->DidInitializeContext(); |
michael@0 | 2630 | |
michael@0 | 2631 | // We wait to fire the debugger hook until the window is all set up and hooked |
michael@0 | 2632 | // up with the outer. See bug 969156. |
michael@0 | 2633 | if (createdInnerWindow) { |
michael@0 | 2634 | JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper()); |
michael@0 | 2635 | JS_FireOnNewGlobalObject(cx, global); |
michael@0 | 2636 | } |
michael@0 | 2637 | |
michael@0 | 2638 | if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { |
michael@0 | 2639 | // We should probably notify. However if this is the, arguably bad, |
michael@0 | 2640 | // situation when we're creating a temporary non-chrome-about-blank |
michael@0 | 2641 | // document in a chrome docshell, don't notify just yet. Instead wait |
michael@0 | 2642 | // until we have a real chrome doc. |
michael@0 | 2643 | if (!mDocShell || |
michael@0 | 2644 | mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome || |
michael@0 | 2645 | nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) { |
michael@0 | 2646 | newInnerWindow->mHasNotifiedGlobalCreated = true; |
michael@0 | 2647 | nsContentUtils::AddScriptRunner( |
michael@0 | 2648 | NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated)); |
michael@0 | 2649 | } |
michael@0 | 2650 | } |
michael@0 | 2651 | |
michael@0 | 2652 | PreloadLocalStorage(); |
michael@0 | 2653 | |
michael@0 | 2654 | return NS_OK; |
michael@0 | 2655 | } |
michael@0 | 2656 | |
michael@0 | 2657 | void |
michael@0 | 2658 | nsGlobalWindow::PreloadLocalStorage() |
michael@0 | 2659 | { |
michael@0 | 2660 | if (!Preferences::GetBool(kStorageEnabled)) { |
michael@0 | 2661 | return; |
michael@0 | 2662 | } |
michael@0 | 2663 | |
michael@0 | 2664 | if (IsChromeWindow()) { |
michael@0 | 2665 | return; |
michael@0 | 2666 | } |
michael@0 | 2667 | |
michael@0 | 2668 | nsIPrincipal* principal = GetPrincipal(); |
michael@0 | 2669 | if (!principal) { |
michael@0 | 2670 | return; |
michael@0 | 2671 | } |
michael@0 | 2672 | |
michael@0 | 2673 | nsresult rv; |
michael@0 | 2674 | nsCOMPtr<nsIURI> firstPartyIsolationURI; |
michael@0 | 2675 | rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI)); |
michael@0 | 2676 | if (NS_FAILED(rv)) { |
michael@0 | 2677 | return; |
michael@0 | 2678 | } |
michael@0 | 2679 | |
michael@0 | 2680 | nsCOMPtr<nsIDOMStorageManager> storageManager = |
michael@0 | 2681 | do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); |
michael@0 | 2682 | if (NS_FAILED(rv)) { |
michael@0 | 2683 | return; |
michael@0 | 2684 | } |
michael@0 | 2685 | |
michael@0 | 2686 | storageManager->PrecacheStorageForFirstParty(firstPartyIsolationURI, principal); |
michael@0 | 2687 | } |
michael@0 | 2688 | |
michael@0 | 2689 | void |
michael@0 | 2690 | nsGlobalWindow::DispatchDOMWindowCreated() |
michael@0 | 2691 | { |
michael@0 | 2692 | if (!mDoc) { |
michael@0 | 2693 | return; |
michael@0 | 2694 | } |
michael@0 | 2695 | |
michael@0 | 2696 | // Fire DOMWindowCreated at chrome event listeners |
michael@0 | 2697 | nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"), |
michael@0 | 2698 | true /* bubbles */, |
michael@0 | 2699 | false /* not cancellable */); |
michael@0 | 2700 | |
michael@0 | 2701 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 2702 | mozilla::services::GetObserverService(); |
michael@0 | 2703 | if (observerService) { |
michael@0 | 2704 | nsAutoString origin; |
michael@0 | 2705 | nsIPrincipal* principal = mDoc->NodePrincipal(); |
michael@0 | 2706 | nsContentUtils::GetUTFOrigin(principal, origin); |
michael@0 | 2707 | observerService-> |
michael@0 | 2708 | NotifyObservers(static_cast<nsIDOMWindow*>(this), |
michael@0 | 2709 | nsContentUtils::IsSystemPrincipal(principal) ? |
michael@0 | 2710 | "chrome-document-global-created" : |
michael@0 | 2711 | "content-document-global-created", |
michael@0 | 2712 | origin.get()); |
michael@0 | 2713 | } |
michael@0 | 2714 | } |
michael@0 | 2715 | |
michael@0 | 2716 | void |
michael@0 | 2717 | nsGlobalWindow::ClearStatus() |
michael@0 | 2718 | { |
michael@0 | 2719 | SetStatus(EmptyString()); |
michael@0 | 2720 | } |
michael@0 | 2721 | |
michael@0 | 2722 | void |
michael@0 | 2723 | nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument) |
michael@0 | 2724 | { |
michael@0 | 2725 | NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows"); |
michael@0 | 2726 | MOZ_ASSERT(aDocument); |
michael@0 | 2727 | |
michael@0 | 2728 | #ifdef PR_LOGGING |
michael@0 | 2729 | if (gDOMLeakPRLog && PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 2730 | nsIURI *uri = aDocument->GetDocumentURI(); |
michael@0 | 2731 | nsAutoCString spec; |
michael@0 | 2732 | if (uri) |
michael@0 | 2733 | uri->GetSpec(spec); |
michael@0 | 2734 | PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get()); |
michael@0 | 2735 | } |
michael@0 | 2736 | #endif |
michael@0 | 2737 | |
michael@0 | 2738 | mDoc = aDocument; |
michael@0 | 2739 | if (IsDOMBinding()) { |
michael@0 | 2740 | WindowBinding::ClearCachedDocumentValue(aCx, this); |
michael@0 | 2741 | } |
michael@0 | 2742 | mFocusedNode = nullptr; |
michael@0 | 2743 | mLocalStorage = nullptr; |
michael@0 | 2744 | mSessionStorage = nullptr; |
michael@0 | 2745 | |
michael@0 | 2746 | #ifdef DEBUG |
michael@0 | 2747 | mLastOpenedURI = aDocument->GetDocumentURI(); |
michael@0 | 2748 | #endif |
michael@0 | 2749 | |
michael@0 | 2750 | Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, |
michael@0 | 2751 | mMutationBits ? 1 : 0); |
michael@0 | 2752 | |
michael@0 | 2753 | // Clear our mutation bitfield. |
michael@0 | 2754 | mMutationBits = 0; |
michael@0 | 2755 | } |
michael@0 | 2756 | |
michael@0 | 2757 | void |
michael@0 | 2758 | nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) |
michael@0 | 2759 | { |
michael@0 | 2760 | NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!"); |
michael@0 | 2761 | MOZ_ASSERT(aDocShell); |
michael@0 | 2762 | |
michael@0 | 2763 | if (aDocShell == mDocShell) { |
michael@0 | 2764 | return; |
michael@0 | 2765 | } |
michael@0 | 2766 | |
michael@0 | 2767 | mDocShell = aDocShell; // Weak Reference |
michael@0 | 2768 | |
michael@0 | 2769 | NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); |
michael@0 | 2770 | |
michael@0 | 2771 | if (mFrames) { |
michael@0 | 2772 | mFrames->SetDocShell(aDocShell); |
michael@0 | 2773 | } |
michael@0 | 2774 | |
michael@0 | 2775 | // Get our enclosing chrome shell and retrieve its global window impl, so |
michael@0 | 2776 | // that we can do some forwarding to the chrome document. |
michael@0 | 2777 | nsCOMPtr<nsIDOMEventTarget> chromeEventHandler; |
michael@0 | 2778 | mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); |
michael@0 | 2779 | mChromeEventHandler = do_QueryInterface(chromeEventHandler); |
michael@0 | 2780 | if (!mChromeEventHandler) { |
michael@0 | 2781 | // We have no chrome event handler. If we have a parent, |
michael@0 | 2782 | // get our chrome event handler from the parent. If |
michael@0 | 2783 | // we don't have a parent, then we need to make a new |
michael@0 | 2784 | // window root object that will function as a chrome event |
michael@0 | 2785 | // handler and receive all events that occur anywhere inside |
michael@0 | 2786 | // our window. |
michael@0 | 2787 | nsCOMPtr<nsIDOMWindow> parentWindow; |
michael@0 | 2788 | GetParent(getter_AddRefs(parentWindow)); |
michael@0 | 2789 | if (parentWindow.get() != static_cast<nsIDOMWindow*>(this)) { |
michael@0 | 2790 | nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(parentWindow)); |
michael@0 | 2791 | mChromeEventHandler = piWindow->GetChromeEventHandler(); |
michael@0 | 2792 | } |
michael@0 | 2793 | else { |
michael@0 | 2794 | mChromeEventHandler = NS_NewWindowRoot(this); |
michael@0 | 2795 | } |
michael@0 | 2796 | } |
michael@0 | 2797 | |
michael@0 | 2798 | bool docShellActive; |
michael@0 | 2799 | mDocShell->GetIsActive(&docShellActive); |
michael@0 | 2800 | mIsBackground = !docShellActive; |
michael@0 | 2801 | } |
michael@0 | 2802 | |
michael@0 | 2803 | void |
michael@0 | 2804 | nsGlobalWindow::DetachFromDocShell() |
michael@0 | 2805 | { |
michael@0 | 2806 | NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!"); |
michael@0 | 2807 | |
michael@0 | 2808 | // DetachFromDocShell means the window is being torn down. Drop our |
michael@0 | 2809 | // reference to the script context, allowing it to be deleted |
michael@0 | 2810 | // later. Meanwhile, keep our weak reference to the script object |
michael@0 | 2811 | // so that it can be retrieved later (until it is finalized by the JS GC). |
michael@0 | 2812 | |
michael@0 | 2813 | NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!"); |
michael@0 | 2814 | |
michael@0 | 2815 | // Call FreeInnerObjects on all inner windows, not just the current |
michael@0 | 2816 | // one, since some could be held by WindowStateHolder objects that |
michael@0 | 2817 | // are GC-owned. |
michael@0 | 2818 | for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this); |
michael@0 | 2819 | inner != this; |
michael@0 | 2820 | inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { |
michael@0 | 2821 | NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, |
michael@0 | 2822 | "bad outer window pointer"); |
michael@0 | 2823 | inner->FreeInnerObjects(); |
michael@0 | 2824 | } |
michael@0 | 2825 | |
michael@0 | 2826 | // Make sure that this is called before we null out the document. |
michael@0 | 2827 | NotifyDOMWindowDestroyed(this); |
michael@0 | 2828 | |
michael@0 | 2829 | NotifyWindowIDDestroyed("outer-window-destroyed"); |
michael@0 | 2830 | |
michael@0 | 2831 | nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); |
michael@0 | 2832 | |
michael@0 | 2833 | if (currentInner) { |
michael@0 | 2834 | NS_ASSERTION(mDoc, "Must have doc!"); |
michael@0 | 2835 | |
michael@0 | 2836 | // Remember the document's principal and URI. |
michael@0 | 2837 | mDocumentPrincipal = mDoc->NodePrincipal(); |
michael@0 | 2838 | mDocumentURI = mDoc->GetDocumentURI(); |
michael@0 | 2839 | mDocBaseURI = mDoc->GetDocBaseURI(); |
michael@0 | 2840 | |
michael@0 | 2841 | // Release our document reference |
michael@0 | 2842 | DropOuterWindowDocs(); |
michael@0 | 2843 | mFocusedNode = nullptr; |
michael@0 | 2844 | } |
michael@0 | 2845 | |
michael@0 | 2846 | ClearControllers(); |
michael@0 | 2847 | |
michael@0 | 2848 | mChromeEventHandler = nullptr; // force release now |
michael@0 | 2849 | |
michael@0 | 2850 | if (mContext) { |
michael@0 | 2851 | mContext->GC(JS::gcreason::SET_DOC_SHELL); |
michael@0 | 2852 | mContext = nullptr; |
michael@0 | 2853 | } |
michael@0 | 2854 | |
michael@0 | 2855 | mDocShell = nullptr; // Weak Reference |
michael@0 | 2856 | |
michael@0 | 2857 | NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); |
michael@0 | 2858 | |
michael@0 | 2859 | if (mFrames) { |
michael@0 | 2860 | mFrames->SetDocShell(nullptr); |
michael@0 | 2861 | } |
michael@0 | 2862 | |
michael@0 | 2863 | MaybeForgiveSpamCount(); |
michael@0 | 2864 | CleanUp(); |
michael@0 | 2865 | } |
michael@0 | 2866 | |
michael@0 | 2867 | void |
michael@0 | 2868 | nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener, |
michael@0 | 2869 | bool aOriginalOpener) |
michael@0 | 2870 | { |
michael@0 | 2871 | FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener)); |
michael@0 | 2872 | |
michael@0 | 2873 | NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled, |
michael@0 | 2874 | "aOriginalOpener is true, but not first call to " |
michael@0 | 2875 | "SetOpenerWindow!"); |
michael@0 | 2876 | NS_ASSERTION(aOpener || !aOriginalOpener, |
michael@0 | 2877 | "Shouldn't set mHadOriginalOpener if aOpener is null"); |
michael@0 | 2878 | |
michael@0 | 2879 | mOpener = do_GetWeakReference(aOpener); |
michael@0 | 2880 | NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!"); |
michael@0 | 2881 | |
michael@0 | 2882 | if (aOriginalOpener) { |
michael@0 | 2883 | mHadOriginalOpener = true; |
michael@0 | 2884 | } |
michael@0 | 2885 | |
michael@0 | 2886 | #ifdef DEBUG |
michael@0 | 2887 | mSetOpenerWindowCalled = true; |
michael@0 | 2888 | #endif |
michael@0 | 2889 | } |
michael@0 | 2890 | |
michael@0 | 2891 | static |
michael@0 | 2892 | already_AddRefed<EventTarget> |
michael@0 | 2893 | TryGetTabChildGlobalAsEventTarget(nsISupports *aFrom) |
michael@0 | 2894 | { |
michael@0 | 2895 | nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(aFrom); |
michael@0 | 2896 | if (!frameLoaderOwner) { |
michael@0 | 2897 | return nullptr; |
michael@0 | 2898 | } |
michael@0 | 2899 | |
michael@0 | 2900 | nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader(); |
michael@0 | 2901 | if (!frameLoader) { |
michael@0 | 2902 | return nullptr; |
michael@0 | 2903 | } |
michael@0 | 2904 | |
michael@0 | 2905 | nsCOMPtr<EventTarget> target = frameLoader->GetTabChildGlobalAsEventTarget(); |
michael@0 | 2906 | return target.forget(); |
michael@0 | 2907 | } |
michael@0 | 2908 | |
michael@0 | 2909 | void |
michael@0 | 2910 | nsGlobalWindow::UpdateParentTarget() |
michael@0 | 2911 | { |
michael@0 | 2912 | // Try to get our frame element's tab child global (its in-process message |
michael@0 | 2913 | // manager). If that fails, fall back to the chrome event handler's tab |
michael@0 | 2914 | // child global, and if it doesn't have one, just use the chrome event |
michael@0 | 2915 | // handler itself. |
michael@0 | 2916 | |
michael@0 | 2917 | nsCOMPtr<Element> frameElement = GetFrameElementInternal(); |
michael@0 | 2918 | nsCOMPtr<EventTarget> eventTarget = |
michael@0 | 2919 | TryGetTabChildGlobalAsEventTarget(frameElement); |
michael@0 | 2920 | |
michael@0 | 2921 | if (!eventTarget) { |
michael@0 | 2922 | nsGlobalWindow* topWin = GetScriptableTop(); |
michael@0 | 2923 | if (topWin) { |
michael@0 | 2924 | frameElement = topWin->GetFrameElementInternal(); |
michael@0 | 2925 | eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement); |
michael@0 | 2926 | } |
michael@0 | 2927 | } |
michael@0 | 2928 | |
michael@0 | 2929 | if (!eventTarget) { |
michael@0 | 2930 | eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler); |
michael@0 | 2931 | } |
michael@0 | 2932 | |
michael@0 | 2933 | if (!eventTarget) { |
michael@0 | 2934 | eventTarget = mChromeEventHandler; |
michael@0 | 2935 | } |
michael@0 | 2936 | |
michael@0 | 2937 | mParentTarget = eventTarget; |
michael@0 | 2938 | } |
michael@0 | 2939 | |
michael@0 | 2940 | EventTarget* |
michael@0 | 2941 | nsGlobalWindow::GetTargetForDOMEvent() |
michael@0 | 2942 | { |
michael@0 | 2943 | return GetOuterWindowInternal(); |
michael@0 | 2944 | } |
michael@0 | 2945 | |
michael@0 | 2946 | EventTarget* |
michael@0 | 2947 | nsGlobalWindow::GetTargetForEventTargetChain() |
michael@0 | 2948 | { |
michael@0 | 2949 | return IsInnerWindow() ? this : GetCurrentInnerWindowInternal(); |
michael@0 | 2950 | } |
michael@0 | 2951 | |
michael@0 | 2952 | nsresult |
michael@0 | 2953 | nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor) |
michael@0 | 2954 | { |
michael@0 | 2955 | return NS_OK; |
michael@0 | 2956 | } |
michael@0 | 2957 | |
michael@0 | 2958 | JSContext* |
michael@0 | 2959 | nsGlobalWindow::GetJSContextForEventHandlers() |
michael@0 | 2960 | { |
michael@0 | 2961 | return nullptr; |
michael@0 | 2962 | } |
michael@0 | 2963 | |
michael@0 | 2964 | nsresult |
michael@0 | 2965 | nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) |
michael@0 | 2966 | { |
michael@0 | 2967 | NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?"); |
michael@0 | 2968 | static uint32_t count = 0; |
michael@0 | 2969 | uint32_t msg = aVisitor.mEvent->message; |
michael@0 | 2970 | |
michael@0 | 2971 | aVisitor.mCanHandle = true; |
michael@0 | 2972 | aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 |
michael@0 | 2973 | if ((msg == NS_MOUSE_MOVE) && gEntropyCollector) { |
michael@0 | 2974 | //Chances are this counter will overflow during the life of the |
michael@0 | 2975 | //process, but that's OK for our case. Means we get a little |
michael@0 | 2976 | //more entropy. |
michael@0 | 2977 | if (count++ % 100 == 0) { |
michael@0 | 2978 | //Since the high bits seem to be zero's most of the time, |
michael@0 | 2979 | //let's only take the lowest half of the point structure. |
michael@0 | 2980 | int16_t myCoord[2]; |
michael@0 | 2981 | |
michael@0 | 2982 | myCoord[0] = aVisitor.mEvent->refPoint.x; |
michael@0 | 2983 | myCoord[1] = aVisitor.mEvent->refPoint.y; |
michael@0 | 2984 | gEntropyCollector->RandomUpdate((void*)myCoord, sizeof(myCoord)); |
michael@0 | 2985 | gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time), |
michael@0 | 2986 | sizeof(uint32_t)); |
michael@0 | 2987 | } |
michael@0 | 2988 | } else if (msg == NS_RESIZE_EVENT) { |
michael@0 | 2989 | mIsHandlingResizeEvent = true; |
michael@0 | 2990 | } else if (msg == NS_MOUSE_BUTTON_DOWN && |
michael@0 | 2991 | aVisitor.mEvent->mFlags.mIsTrusted) { |
michael@0 | 2992 | gMouseDown = true; |
michael@0 | 2993 | } else if ((msg == NS_MOUSE_BUTTON_UP || |
michael@0 | 2994 | msg == NS_DRAGDROP_END) && |
michael@0 | 2995 | aVisitor.mEvent->mFlags.mIsTrusted) { |
michael@0 | 2996 | gMouseDown = false; |
michael@0 | 2997 | if (gDragServiceDisabled) { |
michael@0 | 2998 | nsCOMPtr<nsIDragService> ds = |
michael@0 | 2999 | do_GetService("@mozilla.org/widget/dragservice;1"); |
michael@0 | 3000 | if (ds) { |
michael@0 | 3001 | gDragServiceDisabled = false; |
michael@0 | 3002 | ds->Unsuppress(); |
michael@0 | 3003 | } |
michael@0 | 3004 | } |
michael@0 | 3005 | } |
michael@0 | 3006 | |
michael@0 | 3007 | aVisitor.mParentTarget = GetParentTarget(); |
michael@0 | 3008 | |
michael@0 | 3009 | // Handle 'active' event. |
michael@0 | 3010 | if (!mIdleObservers.IsEmpty() && |
michael@0 | 3011 | aVisitor.mEvent->mFlags.mIsTrusted && |
michael@0 | 3012 | (aVisitor.mEvent->HasMouseEventMessage() || |
michael@0 | 3013 | aVisitor.mEvent->HasDragEventMessage())) { |
michael@0 | 3014 | mAddActiveEventFuzzTime = false; |
michael@0 | 3015 | } |
michael@0 | 3016 | |
michael@0 | 3017 | return NS_OK; |
michael@0 | 3018 | } |
michael@0 | 3019 | |
michael@0 | 3020 | bool |
michael@0 | 3021 | nsGlobalWindow::ShouldPromptToBlockDialogs() |
michael@0 | 3022 | { |
michael@0 | 3023 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 3024 | |
michael@0 | 3025 | nsGlobalWindow *topWindow = GetScriptableTop(); |
michael@0 | 3026 | if (!topWindow) { |
michael@0 | 3027 | NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?"); |
michael@0 | 3028 | return true; |
michael@0 | 3029 | } |
michael@0 | 3030 | |
michael@0 | 3031 | topWindow = topWindow->GetCurrentInnerWindowInternal(); |
michael@0 | 3032 | if (!topWindow) { |
michael@0 | 3033 | return true; |
michael@0 | 3034 | } |
michael@0 | 3035 | |
michael@0 | 3036 | return topWindow->DialogsAreBeingAbused(); |
michael@0 | 3037 | } |
michael@0 | 3038 | |
michael@0 | 3039 | bool |
michael@0 | 3040 | nsGlobalWindow::AreDialogsEnabled() |
michael@0 | 3041 | { |
michael@0 | 3042 | nsGlobalWindow *topWindow = GetScriptableTop(); |
michael@0 | 3043 | if (!topWindow) { |
michael@0 | 3044 | NS_ERROR("AreDialogsEnabled() called without a top window?"); |
michael@0 | 3045 | return false; |
michael@0 | 3046 | } |
michael@0 | 3047 | |
michael@0 | 3048 | // TODO: Warn if no top window? |
michael@0 | 3049 | topWindow = topWindow->GetCurrentInnerWindowInternal(); |
michael@0 | 3050 | if (!topWindow) { |
michael@0 | 3051 | return false; |
michael@0 | 3052 | } |
michael@0 | 3053 | |
michael@0 | 3054 | // Dialogs are blocked if the content viewer is hidden |
michael@0 | 3055 | if (mDocShell) { |
michael@0 | 3056 | nsCOMPtr<nsIContentViewer> cv; |
michael@0 | 3057 | mDocShell->GetContentViewer(getter_AddRefs(cv)); |
michael@0 | 3058 | |
michael@0 | 3059 | bool isHidden; |
michael@0 | 3060 | cv->GetIsHidden(&isHidden); |
michael@0 | 3061 | if (isHidden) { |
michael@0 | 3062 | return false; |
michael@0 | 3063 | } |
michael@0 | 3064 | } |
michael@0 | 3065 | |
michael@0 | 3066 | return topWindow->mAreDialogsEnabled; |
michael@0 | 3067 | } |
michael@0 | 3068 | |
michael@0 | 3069 | bool |
michael@0 | 3070 | nsGlobalWindow::DialogsAreBeingAbused() |
michael@0 | 3071 | { |
michael@0 | 3072 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 3073 | NS_ASSERTION(GetScriptableTop() && |
michael@0 | 3074 | GetScriptableTop()->GetCurrentInnerWindowInternal() == this, |
michael@0 | 3075 | "DialogsAreBeingAbused called with invalid window"); |
michael@0 | 3076 | |
michael@0 | 3077 | if (mLastDialogQuitTime.IsNull() || |
michael@0 | 3078 | nsContentUtils::IsCallerChrome()) { |
michael@0 | 3079 | return false; |
michael@0 | 3080 | } |
michael@0 | 3081 | |
michael@0 | 3082 | TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime); |
michael@0 | 3083 | if (dialogInterval.ToSeconds() < |
michael@0 | 3084 | Preferences::GetInt("dom.successive_dialog_time_limit", |
michael@0 | 3085 | DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) { |
michael@0 | 3086 | mDialogAbuseCount++; |
michael@0 | 3087 | |
michael@0 | 3088 | return GetPopupControlState() > openAllowed || |
michael@0 | 3089 | mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT; |
michael@0 | 3090 | } |
michael@0 | 3091 | |
michael@0 | 3092 | // Reset the abuse counter |
michael@0 | 3093 | mDialogAbuseCount = 0; |
michael@0 | 3094 | |
michael@0 | 3095 | return false; |
michael@0 | 3096 | } |
michael@0 | 3097 | |
michael@0 | 3098 | bool |
michael@0 | 3099 | nsGlobalWindow::ConfirmDialogIfNeeded() |
michael@0 | 3100 | { |
michael@0 | 3101 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 3102 | |
michael@0 | 3103 | NS_ENSURE_TRUE(mDocShell, false); |
michael@0 | 3104 | nsCOMPtr<nsIPromptService> promptSvc = |
michael@0 | 3105 | do_GetService("@mozilla.org/embedcomp/prompt-service;1"); |
michael@0 | 3106 | |
michael@0 | 3107 | if (!promptSvc) { |
michael@0 | 3108 | return true; |
michael@0 | 3109 | } |
michael@0 | 3110 | |
michael@0 | 3111 | // Reset popup state while opening a modal dialog, and firing events |
michael@0 | 3112 | // about the dialog, to prevent the current state from being active |
michael@0 | 3113 | // the whole time a modal dialog is open. |
michael@0 | 3114 | nsAutoPopupStatePusher popupStatePusher(openAbused, true); |
michael@0 | 3115 | |
michael@0 | 3116 | bool disableDialog = false; |
michael@0 | 3117 | nsXPIDLString label, title; |
michael@0 | 3118 | nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, |
michael@0 | 3119 | "ScriptDialogLabel", label); |
michael@0 | 3120 | nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, |
michael@0 | 3121 | "ScriptDialogPreventTitle", title); |
michael@0 | 3122 | promptSvc->Confirm(this, title.get(), label.get(), &disableDialog); |
michael@0 | 3123 | if (disableDialog) { |
michael@0 | 3124 | DisableDialogs(); |
michael@0 | 3125 | return false; |
michael@0 | 3126 | } |
michael@0 | 3127 | |
michael@0 | 3128 | return true; |
michael@0 | 3129 | } |
michael@0 | 3130 | |
michael@0 | 3131 | void |
michael@0 | 3132 | nsGlobalWindow::DisableDialogs() |
michael@0 | 3133 | { |
michael@0 | 3134 | nsGlobalWindow *topWindow = GetScriptableTop(); |
michael@0 | 3135 | if (!topWindow) { |
michael@0 | 3136 | NS_ERROR("DisableDialogs() called without a top window?"); |
michael@0 | 3137 | return; |
michael@0 | 3138 | } |
michael@0 | 3139 | |
michael@0 | 3140 | topWindow = topWindow->GetCurrentInnerWindowInternal(); |
michael@0 | 3141 | // TODO: Warn if no top window? |
michael@0 | 3142 | if (topWindow) { |
michael@0 | 3143 | topWindow->mAreDialogsEnabled = false; |
michael@0 | 3144 | } |
michael@0 | 3145 | } |
michael@0 | 3146 | |
michael@0 | 3147 | void |
michael@0 | 3148 | nsGlobalWindow::EnableDialogs() |
michael@0 | 3149 | { |
michael@0 | 3150 | nsGlobalWindow *topWindow = GetScriptableTop(); |
michael@0 | 3151 | if (!topWindow) { |
michael@0 | 3152 | NS_ERROR("EnableDialogs() called without a top window?"); |
michael@0 | 3153 | return; |
michael@0 | 3154 | } |
michael@0 | 3155 | |
michael@0 | 3156 | // TODO: Warn if no top window? |
michael@0 | 3157 | topWindow = topWindow->GetCurrentInnerWindowInternal(); |
michael@0 | 3158 | if (topWindow) { |
michael@0 | 3159 | topWindow->mAreDialogsEnabled = true; |
michael@0 | 3160 | } |
michael@0 | 3161 | } |
michael@0 | 3162 | |
michael@0 | 3163 | nsresult |
michael@0 | 3164 | nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) |
michael@0 | 3165 | { |
michael@0 | 3166 | NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?"); |
michael@0 | 3167 | |
michael@0 | 3168 | // Return early if there is nothing to do. |
michael@0 | 3169 | switch (aVisitor.mEvent->message) { |
michael@0 | 3170 | case NS_RESIZE_EVENT: |
michael@0 | 3171 | case NS_PAGE_UNLOAD: |
michael@0 | 3172 | case NS_LOAD: |
michael@0 | 3173 | break; |
michael@0 | 3174 | default: |
michael@0 | 3175 | return NS_OK; |
michael@0 | 3176 | } |
michael@0 | 3177 | |
michael@0 | 3178 | /* mChromeEventHandler and mContext go dangling in the middle of this |
michael@0 | 3179 | function under some circumstances (events that destroy the window) |
michael@0 | 3180 | without this addref. */ |
michael@0 | 3181 | nsCOMPtr<nsIDOMEventTarget> kungFuDeathGrip1(mChromeEventHandler); |
michael@0 | 3182 | nsCOMPtr<nsIScriptContext> kungFuDeathGrip2(GetContextInternal()); |
michael@0 | 3183 | |
michael@0 | 3184 | if (aVisitor.mEvent->message == NS_RESIZE_EVENT) { |
michael@0 | 3185 | mIsHandlingResizeEvent = false; |
michael@0 | 3186 | } else if (aVisitor.mEvent->message == NS_PAGE_UNLOAD && |
michael@0 | 3187 | aVisitor.mEvent->mFlags.mIsTrusted) { |
michael@0 | 3188 | // Execute bindingdetached handlers before we tear ourselves |
michael@0 | 3189 | // down. |
michael@0 | 3190 | if (mDoc) { |
michael@0 | 3191 | mDoc->BindingManager()->ExecuteDetachedHandlers(); |
michael@0 | 3192 | } |
michael@0 | 3193 | mIsDocumentLoaded = false; |
michael@0 | 3194 | } else if (aVisitor.mEvent->message == NS_LOAD && |
michael@0 | 3195 | aVisitor.mEvent->mFlags.mIsTrusted) { |
michael@0 | 3196 | // This is page load event since load events don't propagate to |window|. |
michael@0 | 3197 | // @see nsDocument::PreHandleEvent. |
michael@0 | 3198 | mIsDocumentLoaded = true; |
michael@0 | 3199 | |
michael@0 | 3200 | nsCOMPtr<Element> element = GetFrameElementInternal(); |
michael@0 | 3201 | nsIDocShell* docShell = GetDocShell(); |
michael@0 | 3202 | if (element && GetParentInternal() && |
michael@0 | 3203 | docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) { |
michael@0 | 3204 | // If we're not in chrome, or at a chrome boundary, fire the |
michael@0 | 3205 | // onload event for the frame element. |
michael@0 | 3206 | |
michael@0 | 3207 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 3208 | WidgetEvent event(aVisitor.mEvent->mFlags.mIsTrusted, NS_LOAD); |
michael@0 | 3209 | event.mFlags.mBubbles = false; |
michael@0 | 3210 | |
michael@0 | 3211 | // Most of the time we could get a pres context to pass in here, |
michael@0 | 3212 | // but not always (i.e. if this window is not shown there won't |
michael@0 | 3213 | // be a pres context available). Since we're not firing a GUI |
michael@0 | 3214 | // event we don't need a pres context anyway so we just pass |
michael@0 | 3215 | // null as the pres context all the time here. |
michael@0 | 3216 | EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status); |
michael@0 | 3217 | } |
michael@0 | 3218 | } |
michael@0 | 3219 | |
michael@0 | 3220 | return NS_OK; |
michael@0 | 3221 | } |
michael@0 | 3222 | |
michael@0 | 3223 | nsresult |
michael@0 | 3224 | nsGlobalWindow::DispatchDOMEvent(WidgetEvent* aEvent, |
michael@0 | 3225 | nsIDOMEvent* aDOMEvent, |
michael@0 | 3226 | nsPresContext* aPresContext, |
michael@0 | 3227 | nsEventStatus* aEventStatus) |
michael@0 | 3228 | { |
michael@0 | 3229 | return EventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this), |
michael@0 | 3230 | aEvent, aDOMEvent, aPresContext, |
michael@0 | 3231 | aEventStatus); |
michael@0 | 3232 | } |
michael@0 | 3233 | |
michael@0 | 3234 | void |
michael@0 | 3235 | nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject) |
michael@0 | 3236 | { |
michael@0 | 3237 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 3238 | if (aObject == GetWrapperPreserveColor()) { |
michael@0 | 3239 | PoisonWrapper(); |
michael@0 | 3240 | } |
michael@0 | 3241 | } |
michael@0 | 3242 | |
michael@0 | 3243 | nsresult |
michael@0 | 3244 | nsGlobalWindow::SetArguments(nsIArray *aArguments) |
michael@0 | 3245 | { |
michael@0 | 3246 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 3247 | nsresult rv; |
michael@0 | 3248 | |
michael@0 | 3249 | // Historically, we've used the same machinery to handle openDialog arguments |
michael@0 | 3250 | // (exposed via window.arguments) and showModalDialog arguments (exposed via |
michael@0 | 3251 | // window.dialogArguments), even though the former is XUL-only and uses an XPCOM |
michael@0 | 3252 | // array while the latter is web-exposed and uses an arbitrary JS value. |
michael@0 | 3253 | // Moreover, per-spec |dialogArguments| is a property of the browsing context |
michael@0 | 3254 | // (outer), whereas |arguments| lives on the inner. |
michael@0 | 3255 | // |
michael@0 | 3256 | // We've now mostly separated them, but the difference is still opaque to |
michael@0 | 3257 | // nsWindowWatcher (the caller of SetArguments in this little back-and-forth |
michael@0 | 3258 | // embedding waltz we do here). |
michael@0 | 3259 | // |
michael@0 | 3260 | // So we need to demultiplex the two cases here. |
michael@0 | 3261 | nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); |
michael@0 | 3262 | if (mIsModalContentWindow) { |
michael@0 | 3263 | // nsWindowWatcher blindly converts the original nsISupports into an array |
michael@0 | 3264 | // of length 1. We need to recover it, and then cast it back to the concrete |
michael@0 | 3265 | // object we know it to be. |
michael@0 | 3266 | nsCOMPtr<nsISupports> supports = do_QueryElementAt(aArguments, 0, &rv); |
michael@0 | 3267 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3268 | mDialogArguments = static_cast<DialogValueHolder*>(supports.get()); |
michael@0 | 3269 | } else { |
michael@0 | 3270 | mArguments = aArguments; |
michael@0 | 3271 | rv = currentInner->DefineArgumentsProperty(aArguments); |
michael@0 | 3272 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3273 | } |
michael@0 | 3274 | |
michael@0 | 3275 | return NS_OK; |
michael@0 | 3276 | } |
michael@0 | 3277 | |
michael@0 | 3278 | nsresult |
michael@0 | 3279 | nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments) |
michael@0 | 3280 | { |
michael@0 | 3281 | MOZ_ASSERT(!mIsModalContentWindow); // Handled separately. |
michael@0 | 3282 | nsIScriptContext *ctx = GetOuterWindowInternal()->mContext; |
michael@0 | 3283 | NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 3284 | AutoJSContext cx; |
michael@0 | 3285 | |
michael@0 | 3286 | JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor()); |
michael@0 | 3287 | return ctx->SetProperty(obj, "arguments", aArguments); |
michael@0 | 3288 | } |
michael@0 | 3289 | |
michael@0 | 3290 | //***************************************************************************** |
michael@0 | 3291 | // nsGlobalWindow::nsIScriptObjectPrincipal |
michael@0 | 3292 | //***************************************************************************** |
michael@0 | 3293 | |
michael@0 | 3294 | nsIPrincipal* |
michael@0 | 3295 | nsGlobalWindow::GetPrincipal() |
michael@0 | 3296 | { |
michael@0 | 3297 | if (mDoc) { |
michael@0 | 3298 | // If we have a document, get the principal from the document |
michael@0 | 3299 | return mDoc->NodePrincipal(); |
michael@0 | 3300 | } |
michael@0 | 3301 | |
michael@0 | 3302 | if (mDocumentPrincipal) { |
michael@0 | 3303 | return mDocumentPrincipal; |
michael@0 | 3304 | } |
michael@0 | 3305 | |
michael@0 | 3306 | // If we don't have a principal and we don't have a document we |
michael@0 | 3307 | // ask the parent window for the principal. This can happen when |
michael@0 | 3308 | // loading a frameset that has a <frame src="javascript:xxx">, in |
michael@0 | 3309 | // that case the global window is used in JS before we've loaded |
michael@0 | 3310 | // a document into the window. |
michael@0 | 3311 | |
michael@0 | 3312 | nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal = |
michael@0 | 3313 | do_QueryInterface(GetParentInternal()); |
michael@0 | 3314 | |
michael@0 | 3315 | if (objPrincipal) { |
michael@0 | 3316 | return objPrincipal->GetPrincipal(); |
michael@0 | 3317 | } |
michael@0 | 3318 | |
michael@0 | 3319 | return nullptr; |
michael@0 | 3320 | } |
michael@0 | 3321 | |
michael@0 | 3322 | //***************************************************************************** |
michael@0 | 3323 | // nsGlobalWindow::nsIDOMWindow |
michael@0 | 3324 | //***************************************************************************** |
michael@0 | 3325 | |
michael@0 | 3326 | nsIURI* |
michael@0 | 3327 | nsPIDOMWindow::GetDocumentURI() const |
michael@0 | 3328 | { |
michael@0 | 3329 | return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get(); |
michael@0 | 3330 | } |
michael@0 | 3331 | |
michael@0 | 3332 | nsIURI* |
michael@0 | 3333 | nsPIDOMWindow::GetDocBaseURI() const |
michael@0 | 3334 | { |
michael@0 | 3335 | return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get(); |
michael@0 | 3336 | } |
michael@0 | 3337 | |
michael@0 | 3338 | void |
michael@0 | 3339 | nsPIDOMWindow::MaybeCreateDoc() |
michael@0 | 3340 | { |
michael@0 | 3341 | MOZ_ASSERT(!mDoc); |
michael@0 | 3342 | if (nsIDocShell* docShell = GetDocShell()) { |
michael@0 | 3343 | // Note that |document| here is the same thing as our mDoc, but we |
michael@0 | 3344 | // don't have to explicitly set the member variable because the docshell |
michael@0 | 3345 | // has already called SetNewDocument(). |
michael@0 | 3346 | nsCOMPtr<nsIDocument> document = do_GetInterface(docShell); |
michael@0 | 3347 | } |
michael@0 | 3348 | } |
michael@0 | 3349 | |
michael@0 | 3350 | Element* |
michael@0 | 3351 | nsPIDOMWindow::GetFrameElementInternal() const |
michael@0 | 3352 | { |
michael@0 | 3353 | if (mOuterWindow) { |
michael@0 | 3354 | return mOuterWindow->GetFrameElementInternal(); |
michael@0 | 3355 | } |
michael@0 | 3356 | |
michael@0 | 3357 | NS_ASSERTION(!IsInnerWindow(), |
michael@0 | 3358 | "GetFrameElementInternal() called on orphan inner window"); |
michael@0 | 3359 | |
michael@0 | 3360 | return mFrameElement; |
michael@0 | 3361 | } |
michael@0 | 3362 | |
michael@0 | 3363 | void |
michael@0 | 3364 | nsPIDOMWindow::SetFrameElementInternal(Element* aFrameElement) |
michael@0 | 3365 | { |
michael@0 | 3366 | if (IsOuterWindow()) { |
michael@0 | 3367 | mFrameElement = aFrameElement; |
michael@0 | 3368 | |
michael@0 | 3369 | return; |
michael@0 | 3370 | } |
michael@0 | 3371 | |
michael@0 | 3372 | if (!mOuterWindow) { |
michael@0 | 3373 | NS_ERROR("frameElement set on inner window with no outer!"); |
michael@0 | 3374 | |
michael@0 | 3375 | return; |
michael@0 | 3376 | } |
michael@0 | 3377 | |
michael@0 | 3378 | mOuterWindow->SetFrameElementInternal(aFrameElement); |
michael@0 | 3379 | } |
michael@0 | 3380 | |
michael@0 | 3381 | void |
michael@0 | 3382 | nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext) |
michael@0 | 3383 | { |
michael@0 | 3384 | mAudioContexts.AppendElement(aAudioContext); |
michael@0 | 3385 | |
michael@0 | 3386 | nsIDocShell* docShell = GetDocShell(); |
michael@0 | 3387 | if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) { |
michael@0 | 3388 | aAudioContext->Mute(); |
michael@0 | 3389 | } |
michael@0 | 3390 | } |
michael@0 | 3391 | |
michael@0 | 3392 | void |
michael@0 | 3393 | nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext) |
michael@0 | 3394 | { |
michael@0 | 3395 | mAudioContexts.RemoveElement(aAudioContext); |
michael@0 | 3396 | } |
michael@0 | 3397 | |
michael@0 | 3398 | void |
michael@0 | 3399 | nsPIDOMWindow::MuteAudioContexts() |
michael@0 | 3400 | { |
michael@0 | 3401 | for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
michael@0 | 3402 | if (!mAudioContexts[i]->IsOffline()) { |
michael@0 | 3403 | mAudioContexts[i]->Mute(); |
michael@0 | 3404 | } |
michael@0 | 3405 | } |
michael@0 | 3406 | } |
michael@0 | 3407 | |
michael@0 | 3408 | void |
michael@0 | 3409 | nsPIDOMWindow::UnmuteAudioContexts() |
michael@0 | 3410 | { |
michael@0 | 3411 | for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
michael@0 | 3412 | if (!mAudioContexts[i]->IsOffline()) { |
michael@0 | 3413 | mAudioContexts[i]->Unmute(); |
michael@0 | 3414 | } |
michael@0 | 3415 | } |
michael@0 | 3416 | } |
michael@0 | 3417 | |
michael@0 | 3418 | NS_IMETHODIMP |
michael@0 | 3419 | nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument) |
michael@0 | 3420 | { |
michael@0 | 3421 | nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(GetDocument()); |
michael@0 | 3422 | document.forget(aDocument); |
michael@0 | 3423 | return NS_OK; |
michael@0 | 3424 | } |
michael@0 | 3425 | |
michael@0 | 3426 | nsIDOMWindow* |
michael@0 | 3427 | nsGlobalWindow::GetWindow(ErrorResult& aError) |
michael@0 | 3428 | { |
michael@0 | 3429 | FORWARD_TO_OUTER_OR_THROW(GetWindow, (aError), aError, nullptr); |
michael@0 | 3430 | |
michael@0 | 3431 | return this; |
michael@0 | 3432 | } |
michael@0 | 3433 | |
michael@0 | 3434 | NS_IMETHODIMP |
michael@0 | 3435 | nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow) |
michael@0 | 3436 | { |
michael@0 | 3437 | ErrorResult rv; |
michael@0 | 3438 | nsCOMPtr<nsIDOMWindow> window = GetWindow(rv); |
michael@0 | 3439 | window.forget(aWindow); |
michael@0 | 3440 | |
michael@0 | 3441 | return rv.ErrorCode(); |
michael@0 | 3442 | } |
michael@0 | 3443 | |
michael@0 | 3444 | nsIDOMWindow* |
michael@0 | 3445 | nsGlobalWindow::GetSelf(ErrorResult& aError) |
michael@0 | 3446 | { |
michael@0 | 3447 | FORWARD_TO_OUTER_OR_THROW(GetSelf, (aError), aError, nullptr); |
michael@0 | 3448 | |
michael@0 | 3449 | return this; |
michael@0 | 3450 | } |
michael@0 | 3451 | |
michael@0 | 3452 | NS_IMETHODIMP |
michael@0 | 3453 | nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow) |
michael@0 | 3454 | { |
michael@0 | 3455 | ErrorResult rv; |
michael@0 | 3456 | nsCOMPtr<nsIDOMWindow> window = GetSelf(rv); |
michael@0 | 3457 | window.forget(aWindow); |
michael@0 | 3458 | |
michael@0 | 3459 | return rv.ErrorCode(); |
michael@0 | 3460 | } |
michael@0 | 3461 | |
michael@0 | 3462 | Navigator* |
michael@0 | 3463 | nsGlobalWindow::GetNavigator(ErrorResult& aError) |
michael@0 | 3464 | { |
michael@0 | 3465 | FORWARD_TO_INNER_OR_THROW(GetNavigator, (aError), aError, nullptr); |
michael@0 | 3466 | |
michael@0 | 3467 | if (!mNavigator) { |
michael@0 | 3468 | mNavigator = new Navigator(this); |
michael@0 | 3469 | } |
michael@0 | 3470 | |
michael@0 | 3471 | return mNavigator; |
michael@0 | 3472 | } |
michael@0 | 3473 | |
michael@0 | 3474 | NS_IMETHODIMP |
michael@0 | 3475 | nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator) |
michael@0 | 3476 | { |
michael@0 | 3477 | ErrorResult rv; |
michael@0 | 3478 | nsCOMPtr<nsIDOMNavigator> navigator = GetNavigator(rv); |
michael@0 | 3479 | navigator.forget(aNavigator); |
michael@0 | 3480 | |
michael@0 | 3481 | return rv.ErrorCode(); |
michael@0 | 3482 | } |
michael@0 | 3483 | |
michael@0 | 3484 | nsScreen* |
michael@0 | 3485 | nsGlobalWindow::GetScreen(ErrorResult& aError) |
michael@0 | 3486 | { |
michael@0 | 3487 | FORWARD_TO_INNER_OR_THROW(GetScreen, (aError), aError, nullptr); |
michael@0 | 3488 | |
michael@0 | 3489 | if (!mScreen) { |
michael@0 | 3490 | mScreen = nsScreen::Create(this); |
michael@0 | 3491 | if (!mScreen) { |
michael@0 | 3492 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 3493 | return nullptr; |
michael@0 | 3494 | } |
michael@0 | 3495 | } |
michael@0 | 3496 | |
michael@0 | 3497 | return mScreen; |
michael@0 | 3498 | } |
michael@0 | 3499 | |
michael@0 | 3500 | NS_IMETHODIMP |
michael@0 | 3501 | nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen) |
michael@0 | 3502 | { |
michael@0 | 3503 | ErrorResult rv; |
michael@0 | 3504 | nsRefPtr<nsScreen> screen = GetScreen(rv); |
michael@0 | 3505 | screen.forget(aScreen); |
michael@0 | 3506 | |
michael@0 | 3507 | return rv.ErrorCode(); |
michael@0 | 3508 | } |
michael@0 | 3509 | |
michael@0 | 3510 | nsHistory* |
michael@0 | 3511 | nsGlobalWindow::GetHistory(ErrorResult& aError) |
michael@0 | 3512 | { |
michael@0 | 3513 | FORWARD_TO_INNER_OR_THROW(GetHistory, (aError), aError, nullptr); |
michael@0 | 3514 | |
michael@0 | 3515 | if (!mHistory) { |
michael@0 | 3516 | mHistory = new nsHistory(this); |
michael@0 | 3517 | } |
michael@0 | 3518 | |
michael@0 | 3519 | return mHistory; |
michael@0 | 3520 | } |
michael@0 | 3521 | |
michael@0 | 3522 | NS_IMETHODIMP |
michael@0 | 3523 | nsGlobalWindow::GetHistory(nsISupports** aHistory) |
michael@0 | 3524 | { |
michael@0 | 3525 | ErrorResult rv; |
michael@0 | 3526 | nsCOMPtr<nsISupports> history = GetHistory(rv); |
michael@0 | 3527 | history.forget(aHistory); |
michael@0 | 3528 | |
michael@0 | 3529 | return rv.ErrorCode(); |
michael@0 | 3530 | } |
michael@0 | 3531 | |
michael@0 | 3532 | nsPerformance* |
michael@0 | 3533 | nsGlobalWindow::GetPerformance(ErrorResult& aError) |
michael@0 | 3534 | { |
michael@0 | 3535 | FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr); |
michael@0 | 3536 | |
michael@0 | 3537 | nsPerformance* p = nsPIDOMWindow::GetPerformance(); |
michael@0 | 3538 | if (!p) { |
michael@0 | 3539 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 3540 | } |
michael@0 | 3541 | return p; |
michael@0 | 3542 | } |
michael@0 | 3543 | |
michael@0 | 3544 | NS_IMETHODIMP |
michael@0 | 3545 | nsGlobalWindow::GetPerformance(nsISupports** aPerformance) |
michael@0 | 3546 | { |
michael@0 | 3547 | ErrorResult rv; |
michael@0 | 3548 | nsCOMPtr<nsISupports> performance = GetPerformance(rv); |
michael@0 | 3549 | performance.forget(aPerformance); |
michael@0 | 3550 | |
michael@0 | 3551 | return rv.ErrorCode(); |
michael@0 | 3552 | } |
michael@0 | 3553 | |
michael@0 | 3554 | nsPerformance* |
michael@0 | 3555 | nsPIDOMWindow::GetPerformance() |
michael@0 | 3556 | { |
michael@0 | 3557 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 3558 | CreatePerformanceObjectIfNeeded(); |
michael@0 | 3559 | return mPerformance; |
michael@0 | 3560 | } |
michael@0 | 3561 | |
michael@0 | 3562 | void |
michael@0 | 3563 | nsPIDOMWindow::CreatePerformanceObjectIfNeeded() |
michael@0 | 3564 | { |
michael@0 | 3565 | if (mPerformance || !mDoc) { |
michael@0 | 3566 | return; |
michael@0 | 3567 | } |
michael@0 | 3568 | nsRefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming(); |
michael@0 | 3569 | nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel())); |
michael@0 | 3570 | bool timingEnabled = false; |
michael@0 | 3571 | if (!timedChannel || |
michael@0 | 3572 | !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) || |
michael@0 | 3573 | !timingEnabled) { |
michael@0 | 3574 | timedChannel = nullptr; |
michael@0 | 3575 | } |
michael@0 | 3576 | if (timing) { |
michael@0 | 3577 | // If we are dealing with an iframe, we will need the parent's performance |
michael@0 | 3578 | // object (so we can add the iframe as a resource of that page). |
michael@0 | 3579 | nsPerformance* parentPerformance = nullptr; |
michael@0 | 3580 | nsCOMPtr<nsIDOMWindow> parentWindow; |
michael@0 | 3581 | GetScriptableParent(getter_AddRefs(parentWindow)); |
michael@0 | 3582 | nsCOMPtr<nsPIDOMWindow> parentPWindow = do_GetInterface(parentWindow); |
michael@0 | 3583 | if (GetOuterWindow() != parentPWindow) { |
michael@0 | 3584 | if (parentPWindow && !parentPWindow->IsInnerWindow()) { |
michael@0 | 3585 | parentPWindow = parentPWindow->GetCurrentInnerWindow(); |
michael@0 | 3586 | } |
michael@0 | 3587 | if (parentPWindow) { |
michael@0 | 3588 | parentPerformance = parentPWindow->GetPerformance(); |
michael@0 | 3589 | } |
michael@0 | 3590 | } |
michael@0 | 3591 | mPerformance = |
michael@0 | 3592 | new nsPerformance(this, timing, timedChannel, parentPerformance); |
michael@0 | 3593 | } |
michael@0 | 3594 | } |
michael@0 | 3595 | |
michael@0 | 3596 | bool |
michael@0 | 3597 | nsPIDOMWindow::GetAudioMuted() const |
michael@0 | 3598 | { |
michael@0 | 3599 | if (!IsInnerWindow()) { |
michael@0 | 3600 | return mInnerWindow->GetAudioMuted(); |
michael@0 | 3601 | } |
michael@0 | 3602 | |
michael@0 | 3603 | return mAudioMuted; |
michael@0 | 3604 | } |
michael@0 | 3605 | |
michael@0 | 3606 | void |
michael@0 | 3607 | nsPIDOMWindow::SetAudioMuted(bool aMuted) |
michael@0 | 3608 | { |
michael@0 | 3609 | if (!IsInnerWindow()) { |
michael@0 | 3610 | mInnerWindow->SetAudioMuted(aMuted); |
michael@0 | 3611 | return; |
michael@0 | 3612 | } |
michael@0 | 3613 | |
michael@0 | 3614 | if (mAudioMuted == aMuted) { |
michael@0 | 3615 | return; |
michael@0 | 3616 | } |
michael@0 | 3617 | |
michael@0 | 3618 | mAudioMuted = aMuted; |
michael@0 | 3619 | RefreshMediaElements(); |
michael@0 | 3620 | } |
michael@0 | 3621 | |
michael@0 | 3622 | float |
michael@0 | 3623 | nsPIDOMWindow::GetAudioVolume() const |
michael@0 | 3624 | { |
michael@0 | 3625 | if (!IsInnerWindow()) { |
michael@0 | 3626 | return mInnerWindow->GetAudioVolume(); |
michael@0 | 3627 | } |
michael@0 | 3628 | |
michael@0 | 3629 | return mAudioVolume; |
michael@0 | 3630 | } |
michael@0 | 3631 | |
michael@0 | 3632 | nsresult |
michael@0 | 3633 | nsPIDOMWindow::SetAudioVolume(float aVolume) |
michael@0 | 3634 | { |
michael@0 | 3635 | if (!IsInnerWindow()) { |
michael@0 | 3636 | return mInnerWindow->SetAudioVolume(aVolume); |
michael@0 | 3637 | } |
michael@0 | 3638 | |
michael@0 | 3639 | if (aVolume < 0.0) { |
michael@0 | 3640 | return NS_ERROR_DOM_INDEX_SIZE_ERR; |
michael@0 | 3641 | } |
michael@0 | 3642 | |
michael@0 | 3643 | if (mAudioVolume == aVolume) { |
michael@0 | 3644 | return NS_OK; |
michael@0 | 3645 | } |
michael@0 | 3646 | |
michael@0 | 3647 | mAudioVolume = aVolume; |
michael@0 | 3648 | RefreshMediaElements(); |
michael@0 | 3649 | return NS_OK; |
michael@0 | 3650 | } |
michael@0 | 3651 | |
michael@0 | 3652 | float |
michael@0 | 3653 | nsPIDOMWindow::GetAudioGlobalVolume() |
michael@0 | 3654 | { |
michael@0 | 3655 | float globalVolume = 1.0; |
michael@0 | 3656 | nsCOMPtr<nsPIDOMWindow> window = this; |
michael@0 | 3657 | |
michael@0 | 3658 | do { |
michael@0 | 3659 | if (window->GetAudioMuted()) { |
michael@0 | 3660 | return 0; |
michael@0 | 3661 | } |
michael@0 | 3662 | |
michael@0 | 3663 | globalVolume *= window->GetAudioVolume(); |
michael@0 | 3664 | |
michael@0 | 3665 | nsCOMPtr<nsIDOMWindow> win; |
michael@0 | 3666 | window->GetParent(getter_AddRefs(win)); |
michael@0 | 3667 | if (window == win) { |
michael@0 | 3668 | break; |
michael@0 | 3669 | } |
michael@0 | 3670 | |
michael@0 | 3671 | window = do_QueryInterface(win); |
michael@0 | 3672 | |
michael@0 | 3673 | // If there is not parent, or we are the toplevel or the volume is |
michael@0 | 3674 | // already 0.0, we don't continue. |
michael@0 | 3675 | } while (window && window != this && globalVolume); |
michael@0 | 3676 | |
michael@0 | 3677 | return globalVolume; |
michael@0 | 3678 | } |
michael@0 | 3679 | |
michael@0 | 3680 | void |
michael@0 | 3681 | nsPIDOMWindow::RefreshMediaElements() |
michael@0 | 3682 | { |
michael@0 | 3683 | nsRefPtr<AudioChannelService> service = |
michael@0 | 3684 | AudioChannelService::GetAudioChannelService(); |
michael@0 | 3685 | if (service) { |
michael@0 | 3686 | service->RefreshAgentsVolume(this); |
michael@0 | 3687 | } |
michael@0 | 3688 | } |
michael@0 | 3689 | |
michael@0 | 3690 | // nsISpeechSynthesisGetter |
michael@0 | 3691 | |
michael@0 | 3692 | #ifdef MOZ_WEBSPEECH |
michael@0 | 3693 | SpeechSynthesis* |
michael@0 | 3694 | nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError) |
michael@0 | 3695 | { |
michael@0 | 3696 | FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr); |
michael@0 | 3697 | |
michael@0 | 3698 | if (!mSpeechSynthesis) { |
michael@0 | 3699 | mSpeechSynthesis = new SpeechSynthesis(this); |
michael@0 | 3700 | } |
michael@0 | 3701 | |
michael@0 | 3702 | return mSpeechSynthesis; |
michael@0 | 3703 | } |
michael@0 | 3704 | |
michael@0 | 3705 | NS_IMETHODIMP |
michael@0 | 3706 | nsGlobalWindow::GetSpeechSynthesis(nsISupports** aSpeechSynthesis) |
michael@0 | 3707 | { |
michael@0 | 3708 | ErrorResult rv; |
michael@0 | 3709 | nsCOMPtr<nsISupports> speechSynthesis; |
michael@0 | 3710 | if (Preferences::GetBool("media.webspeech.synth.enabled")) { |
michael@0 | 3711 | speechSynthesis = GetSpeechSynthesis(rv); |
michael@0 | 3712 | } |
michael@0 | 3713 | speechSynthesis.forget(aSpeechSynthesis); |
michael@0 | 3714 | |
michael@0 | 3715 | return rv.ErrorCode(); |
michael@0 | 3716 | } |
michael@0 | 3717 | #endif |
michael@0 | 3718 | |
michael@0 | 3719 | already_AddRefed<nsIDOMWindow> |
michael@0 | 3720 | nsGlobalWindow::GetParent(ErrorResult& aError) |
michael@0 | 3721 | { |
michael@0 | 3722 | FORWARD_TO_OUTER_OR_THROW(GetParent, (aError), aError, nullptr); |
michael@0 | 3723 | |
michael@0 | 3724 | if (!mDocShell) { |
michael@0 | 3725 | return nullptr; |
michael@0 | 3726 | } |
michael@0 | 3727 | |
michael@0 | 3728 | nsCOMPtr<nsIDOMWindow> parent; |
michael@0 | 3729 | if (mDocShell->GetIsBrowserOrApp()) { |
michael@0 | 3730 | parent = this; |
michael@0 | 3731 | } else { |
michael@0 | 3732 | aError = GetRealParent(getter_AddRefs(parent)); |
michael@0 | 3733 | } |
michael@0 | 3734 | |
michael@0 | 3735 | return parent.forget(); |
michael@0 | 3736 | } |
michael@0 | 3737 | |
michael@0 | 3738 | /** |
michael@0 | 3739 | * GetScriptableParent is called when script reads window.parent. |
michael@0 | 3740 | * |
michael@0 | 3741 | * In contrast to GetRealParent, GetScriptableParent respects <iframe |
michael@0 | 3742 | * mozbrowser> boundaries, so if |this| is contained by an <iframe |
michael@0 | 3743 | * mozbrowser>, we will return |this| as its own parent. |
michael@0 | 3744 | */ |
michael@0 | 3745 | NS_IMETHODIMP |
michael@0 | 3746 | nsGlobalWindow::GetScriptableParent(nsIDOMWindow** aParent) |
michael@0 | 3747 | { |
michael@0 | 3748 | ErrorResult rv; |
michael@0 | 3749 | nsCOMPtr<nsIDOMWindow> parent = GetParent(rv); |
michael@0 | 3750 | parent.forget(aParent); |
michael@0 | 3751 | |
michael@0 | 3752 | return rv.ErrorCode(); |
michael@0 | 3753 | } |
michael@0 | 3754 | |
michael@0 | 3755 | /** |
michael@0 | 3756 | * nsIDOMWindow::GetParent (when called from C++) is just a wrapper around |
michael@0 | 3757 | * GetRealParent. |
michael@0 | 3758 | */ |
michael@0 | 3759 | NS_IMETHODIMP |
michael@0 | 3760 | nsGlobalWindow::GetRealParent(nsIDOMWindow** aParent) |
michael@0 | 3761 | { |
michael@0 | 3762 | FORWARD_TO_OUTER(GetRealParent, (aParent), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 3763 | |
michael@0 | 3764 | *aParent = nullptr; |
michael@0 | 3765 | if (!mDocShell) { |
michael@0 | 3766 | return NS_OK; |
michael@0 | 3767 | } |
michael@0 | 3768 | |
michael@0 | 3769 | nsCOMPtr<nsIDocShell> parent; |
michael@0 | 3770 | mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)); |
michael@0 | 3771 | |
michael@0 | 3772 | if (parent) { |
michael@0 | 3773 | nsCOMPtr<nsIScriptGlobalObject> globalObject(do_GetInterface(parent)); |
michael@0 | 3774 | NS_ENSURE_SUCCESS(CallQueryInterface(globalObject.get(), aParent), |
michael@0 | 3775 | NS_ERROR_FAILURE); |
michael@0 | 3776 | } |
michael@0 | 3777 | else { |
michael@0 | 3778 | *aParent = static_cast<nsIDOMWindow*>(this); |
michael@0 | 3779 | NS_ADDREF(*aParent); |
michael@0 | 3780 | } |
michael@0 | 3781 | return NS_OK; |
michael@0 | 3782 | } |
michael@0 | 3783 | |
michael@0 | 3784 | static nsresult |
michael@0 | 3785 | GetTopImpl(nsGlobalWindow* aWin, nsIDOMWindow** aTop, bool aScriptable) |
michael@0 | 3786 | { |
michael@0 | 3787 | *aTop = nullptr; |
michael@0 | 3788 | |
michael@0 | 3789 | // Walk up the parent chain. |
michael@0 | 3790 | |
michael@0 | 3791 | nsCOMPtr<nsIDOMWindow> prevParent = aWin; |
michael@0 | 3792 | nsCOMPtr<nsIDOMWindow> parent = aWin; |
michael@0 | 3793 | do { |
michael@0 | 3794 | if (!parent) { |
michael@0 | 3795 | break; |
michael@0 | 3796 | } |
michael@0 | 3797 | |
michael@0 | 3798 | prevParent = parent; |
michael@0 | 3799 | |
michael@0 | 3800 | nsCOMPtr<nsIDOMWindow> newParent; |
michael@0 | 3801 | nsresult rv; |
michael@0 | 3802 | if (aScriptable) { |
michael@0 | 3803 | rv = parent->GetScriptableParent(getter_AddRefs(newParent)); |
michael@0 | 3804 | } |
michael@0 | 3805 | else { |
michael@0 | 3806 | rv = parent->GetParent(getter_AddRefs(newParent)); |
michael@0 | 3807 | } |
michael@0 | 3808 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 3809 | |
michael@0 | 3810 | parent = newParent; |
michael@0 | 3811 | |
michael@0 | 3812 | } while (parent != prevParent); |
michael@0 | 3813 | |
michael@0 | 3814 | if (parent) { |
michael@0 | 3815 | parent.swap(*aTop); |
michael@0 | 3816 | } |
michael@0 | 3817 | |
michael@0 | 3818 | return NS_OK; |
michael@0 | 3819 | } |
michael@0 | 3820 | |
michael@0 | 3821 | /** |
michael@0 | 3822 | * GetScriptableTop is called when script reads window.top. |
michael@0 | 3823 | * |
michael@0 | 3824 | * In contrast to GetRealTop, GetScriptableTop respects <iframe mozbrowser> |
michael@0 | 3825 | * boundaries. If we encounter a window owned by an <iframe mozbrowser> while |
michael@0 | 3826 | * walking up the window hierarchy, we'll stop and return that window. |
michael@0 | 3827 | */ |
michael@0 | 3828 | NS_IMETHODIMP |
michael@0 | 3829 | nsGlobalWindow::GetScriptableTop(nsIDOMWindow **aTop) |
michael@0 | 3830 | { |
michael@0 | 3831 | FORWARD_TO_OUTER(GetScriptableTop, (aTop), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 3832 | return GetTopImpl(this, aTop, /* aScriptable = */ true); |
michael@0 | 3833 | } |
michael@0 | 3834 | |
michael@0 | 3835 | /** |
michael@0 | 3836 | * nsIDOMWindow::GetTop (when called from C++) is just a wrapper around |
michael@0 | 3837 | * GetRealTop. |
michael@0 | 3838 | */ |
michael@0 | 3839 | NS_IMETHODIMP |
michael@0 | 3840 | nsGlobalWindow::GetRealTop(nsIDOMWindow** aTop) |
michael@0 | 3841 | { |
michael@0 | 3842 | nsGlobalWindow* outer; |
michael@0 | 3843 | if (IsInnerWindow()) { |
michael@0 | 3844 | outer = GetOuterWindowInternal(); |
michael@0 | 3845 | if (!outer) { |
michael@0 | 3846 | NS_WARNING("No outer window available!"); |
michael@0 | 3847 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 3848 | } |
michael@0 | 3849 | } else { |
michael@0 | 3850 | outer = this; |
michael@0 | 3851 | } |
michael@0 | 3852 | return GetTopImpl(outer, aTop, /* aScriptable = */ false); |
michael@0 | 3853 | } |
michael@0 | 3854 | |
michael@0 | 3855 | void |
michael@0 | 3856 | nsGlobalWindow::GetContent(JSContext* aCx, |
michael@0 | 3857 | JS::MutableHandle<JSObject*> aRetval, |
michael@0 | 3858 | ErrorResult& aError) |
michael@0 | 3859 | { |
michael@0 | 3860 | FORWARD_TO_OUTER_OR_THROW(GetContent, (aCx, aRetval, aError), aError, ); |
michael@0 | 3861 | |
michael@0 | 3862 | nsCOMPtr<nsIDOMWindow> content = GetContentInternal(aError); |
michael@0 | 3863 | if (aError.Failed()) { |
michael@0 | 3864 | return; |
michael@0 | 3865 | } |
michael@0 | 3866 | |
michael@0 | 3867 | if (content) { |
michael@0 | 3868 | JS::Rooted<JS::Value> val(aCx); |
michael@0 | 3869 | aError = nsContentUtils::WrapNative(aCx, content, &val); |
michael@0 | 3870 | if (aError.Failed()) { |
michael@0 | 3871 | return; |
michael@0 | 3872 | } |
michael@0 | 3873 | |
michael@0 | 3874 | aRetval.set(&val.toObject()); |
michael@0 | 3875 | return; |
michael@0 | 3876 | } |
michael@0 | 3877 | |
michael@0 | 3878 | if (!nsContentUtils::IsCallerChrome() || !IsChromeWindow()) { |
michael@0 | 3879 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 3880 | return; |
michael@0 | 3881 | } |
michael@0 | 3882 | |
michael@0 | 3883 | // Something tries to get .content on a ChromeWindow, try to fetch the CPOW. |
michael@0 | 3884 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); |
michael@0 | 3885 | if (!treeOwner) { |
michael@0 | 3886 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 3887 | return; |
michael@0 | 3888 | } |
michael@0 | 3889 | |
michael@0 | 3890 | JS::Rooted<JS::Value> val(aCx, JS::NullValue()); |
michael@0 | 3891 | aError = treeOwner->GetContentWindow(aCx, &val); |
michael@0 | 3892 | if (aError.Failed()) { |
michael@0 | 3893 | return; |
michael@0 | 3894 | } |
michael@0 | 3895 | |
michael@0 | 3896 | aRetval.set(val.toObjectOrNull()); |
michael@0 | 3897 | } |
michael@0 | 3898 | |
michael@0 | 3899 | already_AddRefed<nsIDOMWindow> |
michael@0 | 3900 | nsGlobalWindow::GetContentInternal(ErrorResult& aError) |
michael@0 | 3901 | { |
michael@0 | 3902 | // First check for a named frame named "content" |
michael@0 | 3903 | nsCOMPtr<nsIDOMWindow> domWindow = |
michael@0 | 3904 | GetChildWindow(NS_LITERAL_STRING("content")); |
michael@0 | 3905 | if (domWindow) { |
michael@0 | 3906 | return domWindow.forget(); |
michael@0 | 3907 | } |
michael@0 | 3908 | |
michael@0 | 3909 | // If we're contained in <iframe mozbrowser> or <iframe mozapp>, then |
michael@0 | 3910 | // GetContent is the same as window.top. |
michael@0 | 3911 | if (mDocShell && mDocShell->GetIsInBrowserOrApp()) { |
michael@0 | 3912 | return GetTop(aError); |
michael@0 | 3913 | } |
michael@0 | 3914 | |
michael@0 | 3915 | nsCOMPtr<nsIDocShellTreeItem> primaryContent; |
michael@0 | 3916 | if (!nsContentUtils::IsCallerChrome()) { |
michael@0 | 3917 | // If we're called by non-chrome code, make sure we don't return |
michael@0 | 3918 | // the primary content window if the calling tab is hidden. In |
michael@0 | 3919 | // such a case we return the same-type root in the hidden tab, |
michael@0 | 3920 | // which is "good enough", for now. |
michael@0 | 3921 | nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(mDocShell)); |
michael@0 | 3922 | |
michael@0 | 3923 | if (baseWin) { |
michael@0 | 3924 | bool visible = false; |
michael@0 | 3925 | baseWin->GetVisibility(&visible); |
michael@0 | 3926 | |
michael@0 | 3927 | if (!visible) { |
michael@0 | 3928 | mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(primaryContent)); |
michael@0 | 3929 | } |
michael@0 | 3930 | } |
michael@0 | 3931 | } |
michael@0 | 3932 | |
michael@0 | 3933 | if (!primaryContent) { |
michael@0 | 3934 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); |
michael@0 | 3935 | if (!treeOwner) { |
michael@0 | 3936 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 3937 | return nullptr; |
michael@0 | 3938 | } |
michael@0 | 3939 | |
michael@0 | 3940 | treeOwner->GetPrimaryContentShell(getter_AddRefs(primaryContent)); |
michael@0 | 3941 | } |
michael@0 | 3942 | |
michael@0 | 3943 | domWindow = do_GetInterface(primaryContent); |
michael@0 | 3944 | return domWindow.forget(); |
michael@0 | 3945 | } |
michael@0 | 3946 | |
michael@0 | 3947 | NS_IMETHODIMP |
michael@0 | 3948 | nsGlobalWindow::GetContent(nsIDOMWindow** aContent) |
michael@0 | 3949 | { |
michael@0 | 3950 | ErrorResult rv; |
michael@0 | 3951 | *aContent = GetContentInternal(rv).take(); |
michael@0 | 3952 | |
michael@0 | 3953 | return rv.ErrorCode(); |
michael@0 | 3954 | } |
michael@0 | 3955 | |
michael@0 | 3956 | NS_IMETHODIMP |
michael@0 | 3957 | nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) |
michael@0 | 3958 | { |
michael@0 | 3959 | ErrorResult rv; |
michael@0 | 3960 | JS::Rooted<JSObject*> content(aCx); |
michael@0 | 3961 | GetContent(aCx, &content, rv); |
michael@0 | 3962 | if (!rv.Failed()) { |
michael@0 | 3963 | aVal.setObjectOrNull(content); |
michael@0 | 3964 | } |
michael@0 | 3965 | |
michael@0 | 3966 | return rv.ErrorCode(); |
michael@0 | 3967 | } |
michael@0 | 3968 | |
michael@0 | 3969 | NS_IMETHODIMP |
michael@0 | 3970 | nsGlobalWindow::GetPrompter(nsIPrompt** aPrompt) |
michael@0 | 3971 | { |
michael@0 | 3972 | if (IsInnerWindow()) { |
michael@0 | 3973 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 3974 | if (!outer) { |
michael@0 | 3975 | NS_WARNING("No outer window available!"); |
michael@0 | 3976 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 3977 | } |
michael@0 | 3978 | return outer->GetPrompter(aPrompt); |
michael@0 | 3979 | } |
michael@0 | 3980 | |
michael@0 | 3981 | if (!mDocShell) |
michael@0 | 3982 | return NS_ERROR_FAILURE; |
michael@0 | 3983 | |
michael@0 | 3984 | nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mDocShell)); |
michael@0 | 3985 | NS_ENSURE_TRUE(prompter, NS_ERROR_NO_INTERFACE); |
michael@0 | 3986 | |
michael@0 | 3987 | NS_ADDREF(*aPrompt = prompter); |
michael@0 | 3988 | return NS_OK; |
michael@0 | 3989 | } |
michael@0 | 3990 | |
michael@0 | 3991 | BarProp* |
michael@0 | 3992 | nsGlobalWindow::GetMenubar(ErrorResult& aError) |
michael@0 | 3993 | { |
michael@0 | 3994 | FORWARD_TO_INNER_OR_THROW(GetMenubar, (aError), aError, nullptr); |
michael@0 | 3995 | |
michael@0 | 3996 | if (!mMenubar) { |
michael@0 | 3997 | mMenubar = new MenubarProp(this); |
michael@0 | 3998 | } |
michael@0 | 3999 | |
michael@0 | 4000 | return mMenubar; |
michael@0 | 4001 | } |
michael@0 | 4002 | |
michael@0 | 4003 | NS_IMETHODIMP |
michael@0 | 4004 | nsGlobalWindow::GetMenubar(nsISupports** aMenubar) |
michael@0 | 4005 | { |
michael@0 | 4006 | ErrorResult rv; |
michael@0 | 4007 | nsCOMPtr<nsISupports> menubar = GetMenubar(rv); |
michael@0 | 4008 | menubar.forget(aMenubar); |
michael@0 | 4009 | |
michael@0 | 4010 | return rv.ErrorCode(); |
michael@0 | 4011 | } |
michael@0 | 4012 | |
michael@0 | 4013 | BarProp* |
michael@0 | 4014 | nsGlobalWindow::GetToolbar(ErrorResult& aError) |
michael@0 | 4015 | { |
michael@0 | 4016 | FORWARD_TO_INNER_OR_THROW(GetToolbar, (aError), aError, nullptr); |
michael@0 | 4017 | |
michael@0 | 4018 | if (!mToolbar) { |
michael@0 | 4019 | mToolbar = new ToolbarProp(this); |
michael@0 | 4020 | } |
michael@0 | 4021 | |
michael@0 | 4022 | return mToolbar; |
michael@0 | 4023 | } |
michael@0 | 4024 | |
michael@0 | 4025 | NS_IMETHODIMP |
michael@0 | 4026 | nsGlobalWindow::GetToolbar(nsISupports** aToolbar) |
michael@0 | 4027 | { |
michael@0 | 4028 | ErrorResult rv; |
michael@0 | 4029 | nsCOMPtr<nsISupports> toolbar = GetToolbar(rv); |
michael@0 | 4030 | toolbar.forget(aToolbar); |
michael@0 | 4031 | |
michael@0 | 4032 | return rv.ErrorCode(); |
michael@0 | 4033 | } |
michael@0 | 4034 | |
michael@0 | 4035 | BarProp* |
michael@0 | 4036 | nsGlobalWindow::GetLocationbar(ErrorResult& aError) |
michael@0 | 4037 | { |
michael@0 | 4038 | FORWARD_TO_INNER_OR_THROW(GetLocationbar, (aError), aError, nullptr); |
michael@0 | 4039 | |
michael@0 | 4040 | if (!mLocationbar) { |
michael@0 | 4041 | mLocationbar = new LocationbarProp(this); |
michael@0 | 4042 | } |
michael@0 | 4043 | return mLocationbar; |
michael@0 | 4044 | } |
michael@0 | 4045 | |
michael@0 | 4046 | NS_IMETHODIMP |
michael@0 | 4047 | nsGlobalWindow::GetLocationbar(nsISupports** aLocationbar) |
michael@0 | 4048 | { |
michael@0 | 4049 | ErrorResult rv; |
michael@0 | 4050 | nsCOMPtr<nsISupports> locationbar = GetLocationbar(rv); |
michael@0 | 4051 | locationbar.forget(aLocationbar); |
michael@0 | 4052 | |
michael@0 | 4053 | return rv.ErrorCode(); |
michael@0 | 4054 | } |
michael@0 | 4055 | |
michael@0 | 4056 | BarProp* |
michael@0 | 4057 | nsGlobalWindow::GetPersonalbar(ErrorResult& aError) |
michael@0 | 4058 | { |
michael@0 | 4059 | FORWARD_TO_INNER_OR_THROW(GetPersonalbar, (aError), aError, nullptr); |
michael@0 | 4060 | |
michael@0 | 4061 | if (!mPersonalbar) { |
michael@0 | 4062 | mPersonalbar = new PersonalbarProp(this); |
michael@0 | 4063 | } |
michael@0 | 4064 | return mPersonalbar; |
michael@0 | 4065 | } |
michael@0 | 4066 | |
michael@0 | 4067 | NS_IMETHODIMP |
michael@0 | 4068 | nsGlobalWindow::GetPersonalbar(nsISupports** aPersonalbar) |
michael@0 | 4069 | { |
michael@0 | 4070 | ErrorResult rv; |
michael@0 | 4071 | nsCOMPtr<nsISupports> personalbar = GetPersonalbar(rv); |
michael@0 | 4072 | personalbar.forget(aPersonalbar); |
michael@0 | 4073 | |
michael@0 | 4074 | return rv.ErrorCode(); |
michael@0 | 4075 | } |
michael@0 | 4076 | |
michael@0 | 4077 | BarProp* |
michael@0 | 4078 | nsGlobalWindow::GetStatusbar(ErrorResult& aError) |
michael@0 | 4079 | { |
michael@0 | 4080 | FORWARD_TO_INNER_OR_THROW(GetStatusbar, (aError), aError, nullptr); |
michael@0 | 4081 | |
michael@0 | 4082 | if (!mStatusbar) { |
michael@0 | 4083 | mStatusbar = new StatusbarProp(this); |
michael@0 | 4084 | } |
michael@0 | 4085 | return mStatusbar; |
michael@0 | 4086 | } |
michael@0 | 4087 | |
michael@0 | 4088 | NS_IMETHODIMP |
michael@0 | 4089 | nsGlobalWindow::GetStatusbar(nsISupports** aStatusbar) |
michael@0 | 4090 | { |
michael@0 | 4091 | ErrorResult rv; |
michael@0 | 4092 | nsCOMPtr<nsISupports> statusbar = GetStatusbar(rv); |
michael@0 | 4093 | statusbar.forget(aStatusbar); |
michael@0 | 4094 | |
michael@0 | 4095 | return rv.ErrorCode(); |
michael@0 | 4096 | } |
michael@0 | 4097 | |
michael@0 | 4098 | BarProp* |
michael@0 | 4099 | nsGlobalWindow::GetScrollbars(ErrorResult& aError) |
michael@0 | 4100 | { |
michael@0 | 4101 | FORWARD_TO_INNER_OR_THROW(GetScrollbars, (aError), aError, nullptr); |
michael@0 | 4102 | |
michael@0 | 4103 | if (!mScrollbars) { |
michael@0 | 4104 | mScrollbars = new ScrollbarsProp(this); |
michael@0 | 4105 | } |
michael@0 | 4106 | |
michael@0 | 4107 | return mScrollbars; |
michael@0 | 4108 | } |
michael@0 | 4109 | |
michael@0 | 4110 | NS_IMETHODIMP |
michael@0 | 4111 | nsGlobalWindow::GetScrollbars(nsISupports** aScrollbars) |
michael@0 | 4112 | { |
michael@0 | 4113 | ErrorResult rv; |
michael@0 | 4114 | nsCOMPtr<nsISupports> scrollbars = GetScrollbars(rv); |
michael@0 | 4115 | scrollbars.forget(aScrollbars); |
michael@0 | 4116 | |
michael@0 | 4117 | return rv.ErrorCode(); |
michael@0 | 4118 | } |
michael@0 | 4119 | |
michael@0 | 4120 | bool |
michael@0 | 4121 | nsGlobalWindow::GetClosed(ErrorResult& aError) |
michael@0 | 4122 | { |
michael@0 | 4123 | FORWARD_TO_OUTER_OR_THROW(GetClosed, (aError), aError, false); |
michael@0 | 4124 | |
michael@0 | 4125 | // If someone called close(), or if we don't have a docshell, we're closed. |
michael@0 | 4126 | return mIsClosed || !mDocShell; |
michael@0 | 4127 | } |
michael@0 | 4128 | |
michael@0 | 4129 | NS_IMETHODIMP |
michael@0 | 4130 | nsGlobalWindow::GetClosed(bool* aClosed) |
michael@0 | 4131 | { |
michael@0 | 4132 | ErrorResult rv; |
michael@0 | 4133 | *aClosed = GetClosed(rv); |
michael@0 | 4134 | |
michael@0 | 4135 | return rv.ErrorCode(); |
michael@0 | 4136 | } |
michael@0 | 4137 | |
michael@0 | 4138 | nsDOMWindowList* |
michael@0 | 4139 | nsGlobalWindow::GetWindowList() |
michael@0 | 4140 | { |
michael@0 | 4141 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 4142 | |
michael@0 | 4143 | if (!mFrames && mDocShell) { |
michael@0 | 4144 | mFrames = new nsDOMWindowList(mDocShell); |
michael@0 | 4145 | } |
michael@0 | 4146 | |
michael@0 | 4147 | return mFrames; |
michael@0 | 4148 | } |
michael@0 | 4149 | |
michael@0 | 4150 | NS_IMETHODIMP |
michael@0 | 4151 | nsGlobalWindow::GetFrames(nsIDOMWindowCollection** aFrames) |
michael@0 | 4152 | { |
michael@0 | 4153 | FORWARD_TO_OUTER(GetFrames, (aFrames), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 4154 | |
michael@0 | 4155 | *aFrames = GetWindowList(); |
michael@0 | 4156 | NS_IF_ADDREF(*aFrames); |
michael@0 | 4157 | return NS_OK; |
michael@0 | 4158 | } |
michael@0 | 4159 | |
michael@0 | 4160 | already_AddRefed<nsIDOMWindow> |
michael@0 | 4161 | nsGlobalWindow::IndexedGetter(uint32_t aIndex, bool& aFound) |
michael@0 | 4162 | { |
michael@0 | 4163 | aFound = false; |
michael@0 | 4164 | |
michael@0 | 4165 | FORWARD_TO_OUTER(IndexedGetter, (aIndex, aFound), nullptr); |
michael@0 | 4166 | |
michael@0 | 4167 | nsDOMWindowList* windows = GetWindowList(); |
michael@0 | 4168 | NS_ENSURE_TRUE(windows, nullptr); |
michael@0 | 4169 | |
michael@0 | 4170 | return windows->IndexedGetter(aIndex, aFound); |
michael@0 | 4171 | } |
michael@0 | 4172 | |
michael@0 | 4173 | void |
michael@0 | 4174 | nsGlobalWindow::GetSupportedNames(nsTArray<nsString>& aNames) |
michael@0 | 4175 | { |
michael@0 | 4176 | FORWARD_TO_OUTER_VOID(GetSupportedNames, (aNames)); |
michael@0 | 4177 | |
michael@0 | 4178 | nsDOMWindowList* windows = GetWindowList(); |
michael@0 | 4179 | if (windows) { |
michael@0 | 4180 | uint32_t length = windows->GetLength(); |
michael@0 | 4181 | nsString* name = aNames.AppendElements(length); |
michael@0 | 4182 | for (uint32_t i = 0; i < length; ++i, ++name) { |
michael@0 | 4183 | nsCOMPtr<nsIDocShellTreeItem> item = |
michael@0 | 4184 | windows->GetDocShellTreeItemAt(i); |
michael@0 | 4185 | item->GetName(*name); |
michael@0 | 4186 | } |
michael@0 | 4187 | } |
michael@0 | 4188 | } |
michael@0 | 4189 | |
michael@0 | 4190 | bool |
michael@0 | 4191 | nsGlobalWindow::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj, |
michael@0 | 4192 | JS::Handle<jsid> aId, |
michael@0 | 4193 | JS::MutableHandle<JSPropertyDescriptor> aDesc) |
michael@0 | 4194 | { |
michael@0 | 4195 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 4196 | |
michael@0 | 4197 | if (!JSID_IS_STRING(aId)) { |
michael@0 | 4198 | return true; |
michael@0 | 4199 | } |
michael@0 | 4200 | |
michael@0 | 4201 | nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc); |
michael@0 | 4202 | if (NS_FAILED(rv)) { |
michael@0 | 4203 | return Throw(aCx, rv); |
michael@0 | 4204 | } |
michael@0 | 4205 | |
michael@0 | 4206 | return true; |
michael@0 | 4207 | } |
michael@0 | 4208 | |
michael@0 | 4209 | struct GlobalNameEnumeratorClosure |
michael@0 | 4210 | { |
michael@0 | 4211 | GlobalNameEnumeratorClosure(JSContext* aCx, nsGlobalWindow* aWindow, |
michael@0 | 4212 | nsTArray<nsString>& aNames) |
michael@0 | 4213 | : mCx(aCx), |
michael@0 | 4214 | mWindow(aWindow), |
michael@0 | 4215 | mWrapper(aCx, aWindow->GetWrapper()), |
michael@0 | 4216 | mNames(aNames) |
michael@0 | 4217 | { |
michael@0 | 4218 | } |
michael@0 | 4219 | |
michael@0 | 4220 | JSContext* mCx; |
michael@0 | 4221 | nsGlobalWindow* mWindow; |
michael@0 | 4222 | JS::Rooted<JSObject*> mWrapper; |
michael@0 | 4223 | nsTArray<nsString>& mNames; |
michael@0 | 4224 | }; |
michael@0 | 4225 | |
michael@0 | 4226 | static PLDHashOperator |
michael@0 | 4227 | EnumerateGlobalName(const nsAString& aName, |
michael@0 | 4228 | const nsGlobalNameStruct& aNameStruct, |
michael@0 | 4229 | void* aClosure) |
michael@0 | 4230 | { |
michael@0 | 4231 | GlobalNameEnumeratorClosure* closure = |
michael@0 | 4232 | static_cast<GlobalNameEnumeratorClosure*>(aClosure); |
michael@0 | 4233 | |
michael@0 | 4234 | if (aNameStruct.mType == nsGlobalNameStruct::eTypeStaticNameSet) { |
michael@0 | 4235 | // We have no idea what names this might install. |
michael@0 | 4236 | return PL_DHASH_NEXT; |
michael@0 | 4237 | } |
michael@0 | 4238 | |
michael@0 | 4239 | if (nsWindowSH::NameStructEnabled(closure->mCx, closure->mWindow, aName, |
michael@0 | 4240 | aNameStruct) && |
michael@0 | 4241 | (!aNameStruct.mConstructorEnabled || |
michael@0 | 4242 | aNameStruct.mConstructorEnabled(closure->mCx, closure->mWrapper))) { |
michael@0 | 4243 | closure->mNames.AppendElement(aName); |
michael@0 | 4244 | } |
michael@0 | 4245 | return PL_DHASH_NEXT; |
michael@0 | 4246 | } |
michael@0 | 4247 | |
michael@0 | 4248 | void |
michael@0 | 4249 | nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames, |
michael@0 | 4250 | ErrorResult& aRv) |
michael@0 | 4251 | { |
michael@0 | 4252 | // "Components" is marked as enumerable but only resolved on demand :-/. |
michael@0 | 4253 | //aNames.AppendElement(NS_LITERAL_STRING("Components")); |
michael@0 | 4254 | |
michael@0 | 4255 | nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager(); |
michael@0 | 4256 | if (nameSpaceManager) { |
michael@0 | 4257 | GlobalNameEnumeratorClosure closure(aCx, this, aNames); |
michael@0 | 4258 | nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &closure); |
michael@0 | 4259 | } |
michael@0 | 4260 | } |
michael@0 | 4261 | |
michael@0 | 4262 | /* static */ bool |
michael@0 | 4263 | nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj) |
michael@0 | 4264 | { |
michael@0 | 4265 | // For now, have to deal with XPConnect objects here. |
michael@0 | 4266 | return xpc::WindowOrNull(aObj)->IsChromeWindow(); |
michael@0 | 4267 | } |
michael@0 | 4268 | |
michael@0 | 4269 | nsIDOMOfflineResourceList* |
michael@0 | 4270 | nsGlobalWindow::GetApplicationCache(ErrorResult& aError) |
michael@0 | 4271 | { |
michael@0 | 4272 | FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr); |
michael@0 | 4273 | |
michael@0 | 4274 | if (!mApplicationCache) { |
michael@0 | 4275 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell())); |
michael@0 | 4276 | if (!webNav) { |
michael@0 | 4277 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 4278 | return nullptr; |
michael@0 | 4279 | } |
michael@0 | 4280 | |
michael@0 | 4281 | nsCOMPtr<nsIURI> uri; |
michael@0 | 4282 | aError = webNav->GetCurrentURI(getter_AddRefs(uri)); |
michael@0 | 4283 | if (aError.Failed()) { |
michael@0 | 4284 | return nullptr; |
michael@0 | 4285 | } |
michael@0 | 4286 | |
michael@0 | 4287 | nsCOMPtr<nsIURI> manifestURI; |
michael@0 | 4288 | nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI)); |
michael@0 | 4289 | |
michael@0 | 4290 | nsRefPtr<nsDOMOfflineResourceList> applicationCache = |
michael@0 | 4291 | new nsDOMOfflineResourceList(manifestURI, uri, this); |
michael@0 | 4292 | |
michael@0 | 4293 | applicationCache->Init(); |
michael@0 | 4294 | |
michael@0 | 4295 | mApplicationCache = applicationCache; |
michael@0 | 4296 | } |
michael@0 | 4297 | |
michael@0 | 4298 | return mApplicationCache; |
michael@0 | 4299 | } |
michael@0 | 4300 | |
michael@0 | 4301 | NS_IMETHODIMP |
michael@0 | 4302 | nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCache) |
michael@0 | 4303 | { |
michael@0 | 4304 | ErrorResult rv; |
michael@0 | 4305 | nsCOMPtr<nsIDOMOfflineResourceList> applicationCache = |
michael@0 | 4306 | GetApplicationCache(rv); |
michael@0 | 4307 | applicationCache.forget(aApplicationCache); |
michael@0 | 4308 | |
michael@0 | 4309 | return rv.ErrorCode(); |
michael@0 | 4310 | } |
michael@0 | 4311 | |
michael@0 | 4312 | nsIDOMCrypto* |
michael@0 | 4313 | nsGlobalWindow::GetCrypto(ErrorResult& aError) |
michael@0 | 4314 | { |
michael@0 | 4315 | FORWARD_TO_INNER_OR_THROW(GetCrypto, (aError), aError, nullptr); |
michael@0 | 4316 | |
michael@0 | 4317 | if (!mCrypto) { |
michael@0 | 4318 | #ifndef MOZ_DISABLE_CRYPTOLEGACY |
michael@0 | 4319 | if (XRE_GetProcessType() != GeckoProcessType_Content) { |
michael@0 | 4320 | nsresult rv; |
michael@0 | 4321 | mCrypto = do_CreateInstance(NS_CRYPTO_CONTRACTID, &rv); |
michael@0 | 4322 | if (NS_FAILED(rv)) { |
michael@0 | 4323 | aError.Throw(rv); |
michael@0 | 4324 | return nullptr; |
michael@0 | 4325 | } |
michael@0 | 4326 | } else |
michael@0 | 4327 | #endif |
michael@0 | 4328 | { |
michael@0 | 4329 | mCrypto = new Crypto(); |
michael@0 | 4330 | } |
michael@0 | 4331 | |
michael@0 | 4332 | mCrypto->Init(this); |
michael@0 | 4333 | } |
michael@0 | 4334 | return mCrypto; |
michael@0 | 4335 | } |
michael@0 | 4336 | |
michael@0 | 4337 | NS_IMETHODIMP |
michael@0 | 4338 | nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto) |
michael@0 | 4339 | { |
michael@0 | 4340 | ErrorResult rv; |
michael@0 | 4341 | nsCOMPtr<nsIDOMCrypto> crypto = GetCrypto(rv); |
michael@0 | 4342 | crypto.forget(aCrypto); |
michael@0 | 4343 | |
michael@0 | 4344 | return rv.ErrorCode(); |
michael@0 | 4345 | } |
michael@0 | 4346 | |
michael@0 | 4347 | nsIControllers* |
michael@0 | 4348 | nsGlobalWindow::GetControllers(ErrorResult& aError) |
michael@0 | 4349 | { |
michael@0 | 4350 | FORWARD_TO_OUTER_OR_THROW(GetControllers, (aError), aError, nullptr); |
michael@0 | 4351 | |
michael@0 | 4352 | if (!mControllers) { |
michael@0 | 4353 | nsresult rv; |
michael@0 | 4354 | mControllers = do_CreateInstance(kXULControllersCID, &rv); |
michael@0 | 4355 | if (NS_FAILED(rv)) { |
michael@0 | 4356 | aError.Throw(rv); |
michael@0 | 4357 | return nullptr; |
michael@0 | 4358 | } |
michael@0 | 4359 | |
michael@0 | 4360 | // Add in the default controller |
michael@0 | 4361 | nsCOMPtr<nsIController> controller = do_CreateInstance( |
michael@0 | 4362 | NS_WINDOWCONTROLLER_CONTRACTID, &rv); |
michael@0 | 4363 | if (NS_FAILED(rv)) { |
michael@0 | 4364 | aError.Throw(rv); |
michael@0 | 4365 | return nullptr; |
michael@0 | 4366 | } |
michael@0 | 4367 | |
michael@0 | 4368 | mControllers->InsertControllerAt(0, controller); |
michael@0 | 4369 | nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller); |
michael@0 | 4370 | if (!controllerContext) { |
michael@0 | 4371 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 4372 | return nullptr; |
michael@0 | 4373 | } |
michael@0 | 4374 | |
michael@0 | 4375 | controllerContext->SetCommandContext(static_cast<nsIDOMWindow*>(this)); |
michael@0 | 4376 | } |
michael@0 | 4377 | |
michael@0 | 4378 | return mControllers; |
michael@0 | 4379 | } |
michael@0 | 4380 | |
michael@0 | 4381 | NS_IMETHODIMP |
michael@0 | 4382 | nsGlobalWindow::GetControllers(nsIControllers** aResult) |
michael@0 | 4383 | { |
michael@0 | 4384 | ErrorResult rv; |
michael@0 | 4385 | nsCOMPtr<nsIControllers> controllers = GetControllers(rv); |
michael@0 | 4386 | controllers.forget(aResult); |
michael@0 | 4387 | |
michael@0 | 4388 | return rv.ErrorCode(); |
michael@0 | 4389 | } |
michael@0 | 4390 | |
michael@0 | 4391 | nsIDOMWindow* |
michael@0 | 4392 | nsGlobalWindow::GetOpenerWindow(ErrorResult& aError) |
michael@0 | 4393 | { |
michael@0 | 4394 | FORWARD_TO_OUTER_OR_THROW(GetOpenerWindow, (aError), aError, nullptr); |
michael@0 | 4395 | |
michael@0 | 4396 | nsCOMPtr<nsPIDOMWindow> opener = do_QueryReferent(mOpener); |
michael@0 | 4397 | if (!opener) { |
michael@0 | 4398 | return nullptr; |
michael@0 | 4399 | } |
michael@0 | 4400 | |
michael@0 | 4401 | // First, check if we were called from a privileged chrome script |
michael@0 | 4402 | if (nsContentUtils::IsCallerChrome()) { |
michael@0 | 4403 | return opener; |
michael@0 | 4404 | } |
michael@0 | 4405 | |
michael@0 | 4406 | // First, ensure that we're not handing back a chrome window. |
michael@0 | 4407 | nsGlobalWindow *win = static_cast<nsGlobalWindow *>(opener.get()); |
michael@0 | 4408 | if (win->IsChromeWindow()) { |
michael@0 | 4409 | return nullptr; |
michael@0 | 4410 | } |
michael@0 | 4411 | |
michael@0 | 4412 | // We don't want to reveal the opener if the opener is a mail window, |
michael@0 | 4413 | // because opener can be used to spoof the contents of a message (bug 105050). |
michael@0 | 4414 | // So, we look in the opener's root docshell to see if it's a mail window. |
michael@0 | 4415 | nsCOMPtr<nsIDocShell> openerDocShell = opener->GetDocShell(); |
michael@0 | 4416 | |
michael@0 | 4417 | if (openerDocShell) { |
michael@0 | 4418 | nsCOMPtr<nsIDocShellTreeItem> openerRootItem; |
michael@0 | 4419 | openerDocShell->GetRootTreeItem(getter_AddRefs(openerRootItem)); |
michael@0 | 4420 | nsCOMPtr<nsIDocShell> openerRootDocShell(do_QueryInterface(openerRootItem)); |
michael@0 | 4421 | if (openerRootDocShell) { |
michael@0 | 4422 | uint32_t appType; |
michael@0 | 4423 | nsresult rv = openerRootDocShell->GetAppType(&appType); |
michael@0 | 4424 | if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) { |
michael@0 | 4425 | return opener; |
michael@0 | 4426 | } |
michael@0 | 4427 | } |
michael@0 | 4428 | } |
michael@0 | 4429 | |
michael@0 | 4430 | return nullptr; |
michael@0 | 4431 | } |
michael@0 | 4432 | |
michael@0 | 4433 | void |
michael@0 | 4434 | nsGlobalWindow::GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval, |
michael@0 | 4435 | ErrorResult& aError) |
michael@0 | 4436 | { |
michael@0 | 4437 | nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(aError); |
michael@0 | 4438 | if (aError.Failed() || !opener) { |
michael@0 | 4439 | aRetval.setNull(); |
michael@0 | 4440 | return; |
michael@0 | 4441 | } |
michael@0 | 4442 | |
michael@0 | 4443 | aError = nsContentUtils::WrapNative(aCx, opener, aRetval); |
michael@0 | 4444 | } |
michael@0 | 4445 | |
michael@0 | 4446 | NS_IMETHODIMP |
michael@0 | 4447 | nsGlobalWindow::GetScriptableOpener(JSContext* aCx, |
michael@0 | 4448 | JS::MutableHandle<JS::Value> aOpener) |
michael@0 | 4449 | { |
michael@0 | 4450 | ErrorResult rv; |
michael@0 | 4451 | GetOpener(aCx, aOpener, rv); |
michael@0 | 4452 | |
michael@0 | 4453 | return rv.ErrorCode(); |
michael@0 | 4454 | } |
michael@0 | 4455 | |
michael@0 | 4456 | NS_IMETHODIMP |
michael@0 | 4457 | nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener) |
michael@0 | 4458 | { |
michael@0 | 4459 | ErrorResult rv; |
michael@0 | 4460 | nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(rv); |
michael@0 | 4461 | opener.forget(aOpener); |
michael@0 | 4462 | return rv.ErrorCode(); |
michael@0 | 4463 | } |
michael@0 | 4464 | |
michael@0 | 4465 | void |
michael@0 | 4466 | nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener, |
michael@0 | 4467 | ErrorResult& aError) |
michael@0 | 4468 | { |
michael@0 | 4469 | // Check if we were called from a privileged chrome script. If not, and if |
michael@0 | 4470 | // aOpener is not null, just define aOpener on our inner window's JS object, |
michael@0 | 4471 | // wrapped into the current compartment so that for Xrays we define on the |
michael@0 | 4472 | // Xray expando object, but don't set it on the outer window, so that it'll |
michael@0 | 4473 | // get reset on navigation. This is just like replaceable properties, but |
michael@0 | 4474 | // we're not quite readonly. |
michael@0 | 4475 | if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) { |
michael@0 | 4476 | JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor()); |
michael@0 | 4477 | if (!thisObj) { |
michael@0 | 4478 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 4479 | return; |
michael@0 | 4480 | } |
michael@0 | 4481 | |
michael@0 | 4482 | if (!JS_WrapObject(aCx, &thisObj) || |
michael@0 | 4483 | !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE, |
michael@0 | 4484 | JS_PropertyStub, JS_StrictPropertyStub)) { |
michael@0 | 4485 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 4486 | } |
michael@0 | 4487 | |
michael@0 | 4488 | return; |
michael@0 | 4489 | } |
michael@0 | 4490 | |
michael@0 | 4491 | if (!aOpener.isObjectOrNull()) { |
michael@0 | 4492 | // Chrome code trying to set some random value as opener |
michael@0 | 4493 | aError.Throw(NS_ERROR_INVALID_ARG); |
michael@0 | 4494 | return; |
michael@0 | 4495 | } |
michael@0 | 4496 | |
michael@0 | 4497 | nsGlobalWindow* win = nullptr; |
michael@0 | 4498 | if (aOpener.isObject()) { |
michael@0 | 4499 | JSObject* unwrapped = js::CheckedUnwrap(&aOpener.toObject(), |
michael@0 | 4500 | /* stopAtOuter = */ false); |
michael@0 | 4501 | if (!unwrapped) { |
michael@0 | 4502 | aError.Throw(NS_ERROR_DOM_SECURITY_ERR); |
michael@0 | 4503 | return; |
michael@0 | 4504 | } |
michael@0 | 4505 | |
michael@0 | 4506 | win = xpc::WindowOrNull(unwrapped); |
michael@0 | 4507 | if (!win) { |
michael@0 | 4508 | // Wasn't a window |
michael@0 | 4509 | aError.Throw(NS_ERROR_INVALID_ARG); |
michael@0 | 4510 | return; |
michael@0 | 4511 | } |
michael@0 | 4512 | } |
michael@0 | 4513 | |
michael@0 | 4514 | SetOpenerWindow(win, false); |
michael@0 | 4515 | } |
michael@0 | 4516 | |
michael@0 | 4517 | NS_IMETHODIMP |
michael@0 | 4518 | nsGlobalWindow::SetScriptableOpener(JSContext* aCx, |
michael@0 | 4519 | JS::Handle<JS::Value> aOpener) |
michael@0 | 4520 | { |
michael@0 | 4521 | ErrorResult rv; |
michael@0 | 4522 | SetOpener(aCx, aOpener, rv); |
michael@0 | 4523 | |
michael@0 | 4524 | return rv.ErrorCode(); |
michael@0 | 4525 | } |
michael@0 | 4526 | |
michael@0 | 4527 | NS_IMETHODIMP |
michael@0 | 4528 | nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener) |
michael@0 | 4529 | { |
michael@0 | 4530 | SetOpenerWindow(aOpener, false); |
michael@0 | 4531 | return NS_OK; |
michael@0 | 4532 | } |
michael@0 | 4533 | |
michael@0 | 4534 | void |
michael@0 | 4535 | nsGlobalWindow::GetStatus(nsAString& aStatus, ErrorResult& aError) |
michael@0 | 4536 | { |
michael@0 | 4537 | FORWARD_TO_OUTER_OR_THROW(GetStatus, (aStatus, aError), aError, ); |
michael@0 | 4538 | |
michael@0 | 4539 | aStatus = mStatus; |
michael@0 | 4540 | } |
michael@0 | 4541 | |
michael@0 | 4542 | NS_IMETHODIMP |
michael@0 | 4543 | nsGlobalWindow::GetStatus(nsAString& aStatus) |
michael@0 | 4544 | { |
michael@0 | 4545 | ErrorResult rv; |
michael@0 | 4546 | GetStatus(aStatus, rv); |
michael@0 | 4547 | |
michael@0 | 4548 | return rv.ErrorCode(); |
michael@0 | 4549 | } |
michael@0 | 4550 | |
michael@0 | 4551 | void |
michael@0 | 4552 | nsGlobalWindow::SetStatus(const nsAString& aStatus, ErrorResult& aError) |
michael@0 | 4553 | { |
michael@0 | 4554 | FORWARD_TO_OUTER_OR_THROW(SetStatus, (aStatus, aError), aError, ); |
michael@0 | 4555 | |
michael@0 | 4556 | mStatus = aStatus; |
michael@0 | 4557 | |
michael@0 | 4558 | /* |
michael@0 | 4559 | * If caller is not chrome and dom.disable_window_status_change is true, |
michael@0 | 4560 | * prevent propagating window.status to the UI by exiting early |
michael@0 | 4561 | */ |
michael@0 | 4562 | |
michael@0 | 4563 | if (!CanSetProperty("dom.disable_window_status_change")) { |
michael@0 | 4564 | return; |
michael@0 | 4565 | } |
michael@0 | 4566 | |
michael@0 | 4567 | nsCOMPtr<nsIWebBrowserChrome> browserChrome = GetWebBrowserChrome(); |
michael@0 | 4568 | if (browserChrome) { |
michael@0 | 4569 | browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT, |
michael@0 | 4570 | PromiseFlatString(aStatus).get()); |
michael@0 | 4571 | } |
michael@0 | 4572 | } |
michael@0 | 4573 | |
michael@0 | 4574 | NS_IMETHODIMP |
michael@0 | 4575 | nsGlobalWindow::SetStatus(const nsAString& aStatus) |
michael@0 | 4576 | { |
michael@0 | 4577 | ErrorResult rv; |
michael@0 | 4578 | SetStatus(aStatus, rv); |
michael@0 | 4579 | |
michael@0 | 4580 | return rv.ErrorCode(); |
michael@0 | 4581 | } |
michael@0 | 4582 | |
michael@0 | 4583 | void |
michael@0 | 4584 | nsGlobalWindow::GetName(nsAString& aName, ErrorResult& aError) |
michael@0 | 4585 | { |
michael@0 | 4586 | FORWARD_TO_OUTER_OR_THROW(GetName, (aName, aError), aError, ); |
michael@0 | 4587 | |
michael@0 | 4588 | if (mDocShell) { |
michael@0 | 4589 | mDocShell->GetName(aName); |
michael@0 | 4590 | } |
michael@0 | 4591 | } |
michael@0 | 4592 | |
michael@0 | 4593 | NS_IMETHODIMP |
michael@0 | 4594 | nsGlobalWindow::GetName(nsAString& aName) |
michael@0 | 4595 | { |
michael@0 | 4596 | ErrorResult rv; |
michael@0 | 4597 | GetName(aName, rv); |
michael@0 | 4598 | |
michael@0 | 4599 | return rv.ErrorCode(); |
michael@0 | 4600 | } |
michael@0 | 4601 | |
michael@0 | 4602 | void |
michael@0 | 4603 | nsGlobalWindow::SetName(const nsAString& aName, mozilla::ErrorResult& aError) |
michael@0 | 4604 | { |
michael@0 | 4605 | FORWARD_TO_OUTER_OR_THROW(SetName, (aName, aError), aError, ); |
michael@0 | 4606 | |
michael@0 | 4607 | if (mDocShell) { |
michael@0 | 4608 | aError = mDocShell->SetName(aName); |
michael@0 | 4609 | } |
michael@0 | 4610 | } |
michael@0 | 4611 | |
michael@0 | 4612 | NS_IMETHODIMP |
michael@0 | 4613 | nsGlobalWindow::SetName(const nsAString& aName) |
michael@0 | 4614 | { |
michael@0 | 4615 | ErrorResult rv; |
michael@0 | 4616 | SetName(aName, rv); |
michael@0 | 4617 | |
michael@0 | 4618 | return rv.ErrorCode(); |
michael@0 | 4619 | } |
michael@0 | 4620 | |
michael@0 | 4621 | // Helper functions used by many methods below. |
michael@0 | 4622 | int32_t |
michael@0 | 4623 | nsGlobalWindow::DevToCSSIntPixels(int32_t px) |
michael@0 | 4624 | { |
michael@0 | 4625 | if (!mDocShell) |
michael@0 | 4626 | return px; // assume 1:1 |
michael@0 | 4627 | |
michael@0 | 4628 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4629 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 4630 | if (!presContext) |
michael@0 | 4631 | return px; |
michael@0 | 4632 | |
michael@0 | 4633 | return presContext->DevPixelsToIntCSSPixels(px); |
michael@0 | 4634 | } |
michael@0 | 4635 | |
michael@0 | 4636 | int32_t |
michael@0 | 4637 | nsGlobalWindow::CSSToDevIntPixels(int32_t px) |
michael@0 | 4638 | { |
michael@0 | 4639 | if (!mDocShell) |
michael@0 | 4640 | return px; // assume 1:1 |
michael@0 | 4641 | |
michael@0 | 4642 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4643 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 4644 | if (!presContext) |
michael@0 | 4645 | return px; |
michael@0 | 4646 | |
michael@0 | 4647 | return presContext->CSSPixelsToDevPixels(px); |
michael@0 | 4648 | } |
michael@0 | 4649 | |
michael@0 | 4650 | nsIntSize |
michael@0 | 4651 | nsGlobalWindow::DevToCSSIntPixels(nsIntSize px) |
michael@0 | 4652 | { |
michael@0 | 4653 | if (!mDocShell) |
michael@0 | 4654 | return px; // assume 1:1 |
michael@0 | 4655 | |
michael@0 | 4656 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4657 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 4658 | if (!presContext) |
michael@0 | 4659 | return px; |
michael@0 | 4660 | |
michael@0 | 4661 | return nsIntSize( |
michael@0 | 4662 | presContext->DevPixelsToIntCSSPixels(px.width), |
michael@0 | 4663 | presContext->DevPixelsToIntCSSPixels(px.height)); |
michael@0 | 4664 | } |
michael@0 | 4665 | |
michael@0 | 4666 | nsIntSize |
michael@0 | 4667 | nsGlobalWindow::CSSToDevIntPixels(nsIntSize px) |
michael@0 | 4668 | { |
michael@0 | 4669 | if (!mDocShell) |
michael@0 | 4670 | return px; // assume 1:1 |
michael@0 | 4671 | |
michael@0 | 4672 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4673 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 4674 | if (!presContext) |
michael@0 | 4675 | return px; |
michael@0 | 4676 | |
michael@0 | 4677 | return nsIntSize( |
michael@0 | 4678 | presContext->CSSPixelsToDevPixels(px.width), |
michael@0 | 4679 | presContext->CSSPixelsToDevPixels(px.height)); |
michael@0 | 4680 | } |
michael@0 | 4681 | |
michael@0 | 4682 | nsresult |
michael@0 | 4683 | nsGlobalWindow::GetInnerSize(CSSIntSize& aSize) |
michael@0 | 4684 | { |
michael@0 | 4685 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 4686 | |
michael@0 | 4687 | EnsureSizeUpToDate(); |
michael@0 | 4688 | |
michael@0 | 4689 | NS_ENSURE_STATE(mDocShell); |
michael@0 | 4690 | |
michael@0 | 4691 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4692 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 4693 | nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 4694 | |
michael@0 | 4695 | if (!presContext || !presShell) { |
michael@0 | 4696 | aSize = CSSIntSize(0, 0); |
michael@0 | 4697 | return NS_OK; |
michael@0 | 4698 | } |
michael@0 | 4699 | |
michael@0 | 4700 | /* |
michael@0 | 4701 | * On platforms with resolution-based zooming, the CSS viewport |
michael@0 | 4702 | * and visual viewport may not be the same. The inner size should |
michael@0 | 4703 | * be the visual viewport, but we fall back to the CSS viewport |
michael@0 | 4704 | * if it is not set. |
michael@0 | 4705 | */ |
michael@0 | 4706 | if (presShell->IsScrollPositionClampingScrollPortSizeSet()) { |
michael@0 | 4707 | aSize = CSSIntRect::FromAppUnitsRounded( |
michael@0 | 4708 | presShell->GetScrollPositionClampingScrollPortSize()); |
michael@0 | 4709 | } else { |
michael@0 | 4710 | nsRefPtr<nsViewManager> viewManager = presShell->GetViewManager(); |
michael@0 | 4711 | if (viewManager) { |
michael@0 | 4712 | viewManager->FlushDelayedResize(false); |
michael@0 | 4713 | } |
michael@0 | 4714 | |
michael@0 | 4715 | aSize = CSSIntRect::FromAppUnitsRounded( |
michael@0 | 4716 | presContext->GetVisibleArea().Size()); |
michael@0 | 4717 | } |
michael@0 | 4718 | return NS_OK; |
michael@0 | 4719 | } |
michael@0 | 4720 | |
michael@0 | 4721 | int32_t |
michael@0 | 4722 | nsGlobalWindow::GetInnerWidth(ErrorResult& aError) |
michael@0 | 4723 | { |
michael@0 | 4724 | FORWARD_TO_OUTER_OR_THROW(GetInnerWidth, (aError), aError, 0); |
michael@0 | 4725 | |
michael@0 | 4726 | CSSIntSize size; |
michael@0 | 4727 | aError = GetInnerSize(size); |
michael@0 | 4728 | return size.width; |
michael@0 | 4729 | } |
michael@0 | 4730 | |
michael@0 | 4731 | NS_IMETHODIMP |
michael@0 | 4732 | nsGlobalWindow::GetInnerWidth(int32_t* aInnerWidth) |
michael@0 | 4733 | { |
michael@0 | 4734 | ErrorResult rv; |
michael@0 | 4735 | *aInnerWidth = GetInnerWidth(rv); |
michael@0 | 4736 | |
michael@0 | 4737 | return rv.ErrorCode(); |
michael@0 | 4738 | } |
michael@0 | 4739 | |
michael@0 | 4740 | void |
michael@0 | 4741 | nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth, ErrorResult& aError) |
michael@0 | 4742 | { |
michael@0 | 4743 | FORWARD_TO_OUTER_OR_THROW(SetInnerWidth, (aInnerWidth, aError), aError, ); |
michael@0 | 4744 | |
michael@0 | 4745 | if (!mDocShell) { |
michael@0 | 4746 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 4747 | return; |
michael@0 | 4748 | } |
michael@0 | 4749 | |
michael@0 | 4750 | /* |
michael@0 | 4751 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 4752 | * prevent setting window.innerWidth by exiting early |
michael@0 | 4753 | */ |
michael@0 | 4754 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 4755 | return; |
michael@0 | 4756 | } |
michael@0 | 4757 | |
michael@0 | 4758 | CheckSecurityWidthAndHeight(&aInnerWidth, nullptr); |
michael@0 | 4759 | |
michael@0 | 4760 | nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 4761 | |
michael@0 | 4762 | if (presShell && presShell->GetIsViewportOverridden()) |
michael@0 | 4763 | { |
michael@0 | 4764 | nscoord height = 0; |
michael@0 | 4765 | |
michael@0 | 4766 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4767 | presContext = presShell->GetPresContext(); |
michael@0 | 4768 | |
michael@0 | 4769 | nsRect shellArea = presContext->GetVisibleArea(); |
michael@0 | 4770 | height = shellArea.height; |
michael@0 | 4771 | SetCSSViewportWidthAndHeight(nsPresContext::CSSPixelsToAppUnits(aInnerWidth), |
michael@0 | 4772 | height); |
michael@0 | 4773 | return; |
michael@0 | 4774 | } |
michael@0 | 4775 | |
michael@0 | 4776 | int32_t height = 0; |
michael@0 | 4777 | int32_t unused = 0; |
michael@0 | 4778 | |
michael@0 | 4779 | nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell)); |
michael@0 | 4780 | docShellAsWin->GetSize(&unused, &height); |
michael@0 | 4781 | aError = SetDocShellWidthAndHeight(CSSToDevIntPixels(aInnerWidth), height); |
michael@0 | 4782 | } |
michael@0 | 4783 | |
michael@0 | 4784 | NS_IMETHODIMP |
michael@0 | 4785 | nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth) |
michael@0 | 4786 | { |
michael@0 | 4787 | ErrorResult rv; |
michael@0 | 4788 | SetInnerWidth(aInnerWidth, rv); |
michael@0 | 4789 | |
michael@0 | 4790 | return rv.ErrorCode(); |
michael@0 | 4791 | } |
michael@0 | 4792 | |
michael@0 | 4793 | int32_t |
michael@0 | 4794 | nsGlobalWindow::GetInnerHeight(ErrorResult& aError) |
michael@0 | 4795 | { |
michael@0 | 4796 | FORWARD_TO_OUTER_OR_THROW(GetInnerHeight, (aError), aError, 0); |
michael@0 | 4797 | |
michael@0 | 4798 | CSSIntSize size; |
michael@0 | 4799 | aError = GetInnerSize(size); |
michael@0 | 4800 | return size.height; |
michael@0 | 4801 | } |
michael@0 | 4802 | |
michael@0 | 4803 | NS_IMETHODIMP |
michael@0 | 4804 | nsGlobalWindow::GetInnerHeight(int32_t* aInnerHeight) |
michael@0 | 4805 | { |
michael@0 | 4806 | ErrorResult rv; |
michael@0 | 4807 | *aInnerHeight = GetInnerHeight(rv); |
michael@0 | 4808 | |
michael@0 | 4809 | return rv.ErrorCode(); |
michael@0 | 4810 | } |
michael@0 | 4811 | |
michael@0 | 4812 | void |
michael@0 | 4813 | nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight, ErrorResult& aError) |
michael@0 | 4814 | { |
michael@0 | 4815 | FORWARD_TO_OUTER_OR_THROW(SetInnerHeight, (aInnerHeight, aError), aError, ); |
michael@0 | 4816 | |
michael@0 | 4817 | if (!mDocShell) { |
michael@0 | 4818 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 4819 | return; |
michael@0 | 4820 | } |
michael@0 | 4821 | |
michael@0 | 4822 | /* |
michael@0 | 4823 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 4824 | * prevent setting window.innerHeight by exiting early |
michael@0 | 4825 | */ |
michael@0 | 4826 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 4827 | return; |
michael@0 | 4828 | } |
michael@0 | 4829 | |
michael@0 | 4830 | nsRefPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 4831 | |
michael@0 | 4832 | if (presShell && presShell->GetIsViewportOverridden()) |
michael@0 | 4833 | { |
michael@0 | 4834 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 4835 | presContext = presShell->GetPresContext(); |
michael@0 | 4836 | |
michael@0 | 4837 | nsRect shellArea = presContext->GetVisibleArea(); |
michael@0 | 4838 | nscoord height = aInnerHeight; |
michael@0 | 4839 | nscoord width = shellArea.width; |
michael@0 | 4840 | CheckSecurityWidthAndHeight(nullptr, &height); |
michael@0 | 4841 | SetCSSViewportWidthAndHeight(width, |
michael@0 | 4842 | nsPresContext::CSSPixelsToAppUnits(height)); |
michael@0 | 4843 | return; |
michael@0 | 4844 | } |
michael@0 | 4845 | |
michael@0 | 4846 | int32_t height = 0; |
michael@0 | 4847 | int32_t width = 0; |
michael@0 | 4848 | |
michael@0 | 4849 | nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell)); |
michael@0 | 4850 | docShellAsWin->GetSize(&width, &height); |
michael@0 | 4851 | CheckSecurityWidthAndHeight(nullptr, &aInnerHeight); |
michael@0 | 4852 | aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight)); |
michael@0 | 4853 | } |
michael@0 | 4854 | |
michael@0 | 4855 | NS_IMETHODIMP |
michael@0 | 4856 | nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight) |
michael@0 | 4857 | { |
michael@0 | 4858 | ErrorResult rv; |
michael@0 | 4859 | SetInnerHeight(aInnerHeight, rv); |
michael@0 | 4860 | |
michael@0 | 4861 | return rv.ErrorCode(); |
michael@0 | 4862 | } |
michael@0 | 4863 | |
michael@0 | 4864 | nsIntSize |
michael@0 | 4865 | nsGlobalWindow::GetOuterSize(ErrorResult& aError) |
michael@0 | 4866 | { |
michael@0 | 4867 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 4868 | |
michael@0 | 4869 | if (!IsChrome()) { |
michael@0 | 4870 | CSSIntSize size; |
michael@0 | 4871 | aError = GetInnerSize(size); |
michael@0 | 4872 | return nsIntSize(size.width, size.height); |
michael@0 | 4873 | } |
michael@0 | 4874 | |
michael@0 | 4875 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 4876 | if (!treeOwnerAsWin) { |
michael@0 | 4877 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 4878 | return nsIntSize(0, 0); |
michael@0 | 4879 | } |
michael@0 | 4880 | |
michael@0 | 4881 | nsGlobalWindow* rootWindow = |
michael@0 | 4882 | static_cast<nsGlobalWindow *>(GetPrivateRoot()); |
michael@0 | 4883 | if (rootWindow) { |
michael@0 | 4884 | rootWindow->FlushPendingNotifications(Flush_Layout); |
michael@0 | 4885 | } |
michael@0 | 4886 | |
michael@0 | 4887 | nsIntSize sizeDevPixels; |
michael@0 | 4888 | aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height); |
michael@0 | 4889 | if (aError.Failed()) { |
michael@0 | 4890 | return nsIntSize(); |
michael@0 | 4891 | } |
michael@0 | 4892 | |
michael@0 | 4893 | return DevToCSSIntPixels(sizeDevPixels); |
michael@0 | 4894 | } |
michael@0 | 4895 | |
michael@0 | 4896 | int32_t |
michael@0 | 4897 | nsGlobalWindow::GetOuterWidth(ErrorResult& aError) |
michael@0 | 4898 | { |
michael@0 | 4899 | FORWARD_TO_OUTER_OR_THROW(GetOuterWidth, (aError), aError, 0); |
michael@0 | 4900 | return GetOuterSize(aError).width; |
michael@0 | 4901 | } |
michael@0 | 4902 | |
michael@0 | 4903 | NS_IMETHODIMP |
michael@0 | 4904 | nsGlobalWindow::GetOuterWidth(int32_t* aOuterWidth) |
michael@0 | 4905 | { |
michael@0 | 4906 | ErrorResult rv; |
michael@0 | 4907 | *aOuterWidth = GetOuterWidth(rv); |
michael@0 | 4908 | |
michael@0 | 4909 | return rv.ErrorCode(); |
michael@0 | 4910 | } |
michael@0 | 4911 | |
michael@0 | 4912 | int32_t |
michael@0 | 4913 | nsGlobalWindow::GetOuterHeight(ErrorResult& aError) |
michael@0 | 4914 | { |
michael@0 | 4915 | FORWARD_TO_OUTER_OR_THROW(GetOuterHeight, (aError), aError, 0); |
michael@0 | 4916 | return GetOuterSize(aError).height; |
michael@0 | 4917 | } |
michael@0 | 4918 | |
michael@0 | 4919 | NS_IMETHODIMP |
michael@0 | 4920 | nsGlobalWindow::GetOuterHeight(int32_t* aOuterHeight) |
michael@0 | 4921 | { |
michael@0 | 4922 | ErrorResult rv; |
michael@0 | 4923 | *aOuterHeight = GetOuterHeight(rv); |
michael@0 | 4924 | |
michael@0 | 4925 | return rv.ErrorCode(); |
michael@0 | 4926 | } |
michael@0 | 4927 | |
michael@0 | 4928 | void |
michael@0 | 4929 | nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth, |
michael@0 | 4930 | ErrorResult& aError) |
michael@0 | 4931 | { |
michael@0 | 4932 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 4933 | |
michael@0 | 4934 | /* |
michael@0 | 4935 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 4936 | * prevent setting window.outerWidth by exiting early |
michael@0 | 4937 | */ |
michael@0 | 4938 | |
michael@0 | 4939 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 4940 | return; |
michael@0 | 4941 | } |
michael@0 | 4942 | |
michael@0 | 4943 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 4944 | if (!treeOwnerAsWin) { |
michael@0 | 4945 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 4946 | return; |
michael@0 | 4947 | } |
michael@0 | 4948 | |
michael@0 | 4949 | CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr, |
michael@0 | 4950 | aIsWidth ? nullptr : &aLengthCSSPixels); |
michael@0 | 4951 | |
michael@0 | 4952 | int32_t width, height; |
michael@0 | 4953 | aError = treeOwnerAsWin->GetSize(&width, &height); |
michael@0 | 4954 | if (aError.Failed()) { |
michael@0 | 4955 | return; |
michael@0 | 4956 | } |
michael@0 | 4957 | |
michael@0 | 4958 | int32_t lengthDevPixels = CSSToDevIntPixels(aLengthCSSPixels); |
michael@0 | 4959 | if (aIsWidth) { |
michael@0 | 4960 | width = lengthDevPixels; |
michael@0 | 4961 | } else { |
michael@0 | 4962 | height = lengthDevPixels; |
michael@0 | 4963 | } |
michael@0 | 4964 | aError = treeOwnerAsWin->SetSize(width, height, true); |
michael@0 | 4965 | } |
michael@0 | 4966 | |
michael@0 | 4967 | void |
michael@0 | 4968 | nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth, ErrorResult& aError) |
michael@0 | 4969 | { |
michael@0 | 4970 | FORWARD_TO_OUTER_OR_THROW(SetOuterWidth, (aOuterWidth, aError), aError, ); |
michael@0 | 4971 | |
michael@0 | 4972 | SetOuterSize(aOuterWidth, true, aError); |
michael@0 | 4973 | } |
michael@0 | 4974 | |
michael@0 | 4975 | NS_IMETHODIMP |
michael@0 | 4976 | nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth) |
michael@0 | 4977 | { |
michael@0 | 4978 | ErrorResult rv; |
michael@0 | 4979 | SetOuterWidth(aOuterWidth, rv); |
michael@0 | 4980 | |
michael@0 | 4981 | return rv.ErrorCode(); |
michael@0 | 4982 | } |
michael@0 | 4983 | |
michael@0 | 4984 | void |
michael@0 | 4985 | nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight, ErrorResult& aError) |
michael@0 | 4986 | { |
michael@0 | 4987 | FORWARD_TO_OUTER_OR_THROW(SetOuterHeight, (aOuterHeight, aError), aError, ); |
michael@0 | 4988 | |
michael@0 | 4989 | SetOuterSize(aOuterHeight, false, aError); |
michael@0 | 4990 | } |
michael@0 | 4991 | |
michael@0 | 4992 | NS_IMETHODIMP |
michael@0 | 4993 | nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight) |
michael@0 | 4994 | { |
michael@0 | 4995 | ErrorResult rv; |
michael@0 | 4996 | SetOuterHeight(aOuterHeight, rv); |
michael@0 | 4997 | |
michael@0 | 4998 | return rv.ErrorCode(); |
michael@0 | 4999 | } |
michael@0 | 5000 | |
michael@0 | 5001 | nsIntPoint |
michael@0 | 5002 | nsGlobalWindow::GetScreenXY(ErrorResult& aError) |
michael@0 | 5003 | { |
michael@0 | 5004 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 5005 | |
michael@0 | 5006 | // For non-chrome callers, always return (0,0) to prevent fingerprinting. |
michael@0 | 5007 | if (!IsChrome()) { |
michael@0 | 5008 | return nsIntPoint(0, 0); |
michael@0 | 5009 | } |
michael@0 | 5010 | |
michael@0 | 5011 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 5012 | if (!treeOwnerAsWin) { |
michael@0 | 5013 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 5014 | return nsIntPoint(0, 0); |
michael@0 | 5015 | } |
michael@0 | 5016 | |
michael@0 | 5017 | int32_t x = 0, y = 0; |
michael@0 | 5018 | aError = treeOwnerAsWin->GetPosition(&x, &y); |
michael@0 | 5019 | return nsIntPoint(x, y); |
michael@0 | 5020 | } |
michael@0 | 5021 | |
michael@0 | 5022 | int32_t |
michael@0 | 5023 | nsGlobalWindow::GetScreenX(ErrorResult& aError) |
michael@0 | 5024 | { |
michael@0 | 5025 | FORWARD_TO_OUTER_OR_THROW(GetScreenX, (aError), aError, 0); |
michael@0 | 5026 | |
michael@0 | 5027 | return DevToCSSIntPixels(GetScreenXY(aError).x); |
michael@0 | 5028 | } |
michael@0 | 5029 | |
michael@0 | 5030 | NS_IMETHODIMP |
michael@0 | 5031 | nsGlobalWindow::GetScreenX(int32_t* aScreenX) |
michael@0 | 5032 | { |
michael@0 | 5033 | ErrorResult rv; |
michael@0 | 5034 | *aScreenX = GetScreenX(rv); |
michael@0 | 5035 | |
michael@0 | 5036 | return rv.ErrorCode(); |
michael@0 | 5037 | } |
michael@0 | 5038 | |
michael@0 | 5039 | nsRect |
michael@0 | 5040 | nsGlobalWindow::GetInnerScreenRect() |
michael@0 | 5041 | { |
michael@0 | 5042 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 5043 | |
michael@0 | 5044 | if (!mDocShell) { |
michael@0 | 5045 | return nsRect(); |
michael@0 | 5046 | } |
michael@0 | 5047 | |
michael@0 | 5048 | nsGlobalWindow* rootWindow = |
michael@0 | 5049 | static_cast<nsGlobalWindow*>(GetPrivateRoot()); |
michael@0 | 5050 | if (rootWindow) { |
michael@0 | 5051 | rootWindow->FlushPendingNotifications(Flush_Layout); |
michael@0 | 5052 | } |
michael@0 | 5053 | |
michael@0 | 5054 | if (!mDocShell) { |
michael@0 | 5055 | return nsRect(); |
michael@0 | 5056 | } |
michael@0 | 5057 | |
michael@0 | 5058 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 5059 | if (!presShell) { |
michael@0 | 5060 | return nsRect(); |
michael@0 | 5061 | } |
michael@0 | 5062 | nsIFrame* rootFrame = presShell->GetRootFrame(); |
michael@0 | 5063 | if (!rootFrame) { |
michael@0 | 5064 | return nsRect(); |
michael@0 | 5065 | } |
michael@0 | 5066 | |
michael@0 | 5067 | return rootFrame->GetScreenRectInAppUnits(); |
michael@0 | 5068 | } |
michael@0 | 5069 | |
michael@0 | 5070 | float |
michael@0 | 5071 | nsGlobalWindow::GetMozInnerScreenX(ErrorResult& aError) |
michael@0 | 5072 | { |
michael@0 | 5073 | FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenX, (aError), aError, 0); |
michael@0 | 5074 | |
michael@0 | 5075 | // For non-chrome callers, always return 0 to prevent fingerprinting. |
michael@0 | 5076 | if (!IsChrome()) return 0.0; |
michael@0 | 5077 | |
michael@0 | 5078 | nsRect r = GetInnerScreenRect(); |
michael@0 | 5079 | return nsPresContext::AppUnitsToFloatCSSPixels(r.x); |
michael@0 | 5080 | } |
michael@0 | 5081 | |
michael@0 | 5082 | NS_IMETHODIMP |
michael@0 | 5083 | nsGlobalWindow::GetMozInnerScreenX(float* aScreenX) |
michael@0 | 5084 | { |
michael@0 | 5085 | ErrorResult rv; |
michael@0 | 5086 | *aScreenX = GetMozInnerScreenX(rv); |
michael@0 | 5087 | |
michael@0 | 5088 | return rv.ErrorCode(); |
michael@0 | 5089 | } |
michael@0 | 5090 | |
michael@0 | 5091 | float |
michael@0 | 5092 | nsGlobalWindow::GetMozInnerScreenY(ErrorResult& aError) |
michael@0 | 5093 | { |
michael@0 | 5094 | FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenY, (aError), aError, 0); |
michael@0 | 5095 | |
michael@0 | 5096 | // For non-chrome callers, always return 0 to prevent fingerprinting. |
michael@0 | 5097 | if (!IsChrome()) return 0.0; |
michael@0 | 5098 | |
michael@0 | 5099 | nsRect r = GetInnerScreenRect(); |
michael@0 | 5100 | return nsPresContext::AppUnitsToFloatCSSPixels(r.y); |
michael@0 | 5101 | } |
michael@0 | 5102 | |
michael@0 | 5103 | NS_IMETHODIMP |
michael@0 | 5104 | nsGlobalWindow::GetMozInnerScreenY(float* aScreenY) |
michael@0 | 5105 | { |
michael@0 | 5106 | ErrorResult rv; |
michael@0 | 5107 | *aScreenY = GetMozInnerScreenY(rv); |
michael@0 | 5108 | |
michael@0 | 5109 | return rv.ErrorCode(); |
michael@0 | 5110 | } |
michael@0 | 5111 | |
michael@0 | 5112 | float |
michael@0 | 5113 | nsGlobalWindow::GetDevicePixelRatio(ErrorResult& aError) |
michael@0 | 5114 | { |
michael@0 | 5115 | FORWARD_TO_OUTER_OR_THROW(GetDevicePixelRatio, (aError), aError, 0.0); |
michael@0 | 5116 | |
michael@0 | 5117 | if (!mDocShell) { |
michael@0 | 5118 | return 1.0; |
michael@0 | 5119 | } |
michael@0 | 5120 | |
michael@0 | 5121 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 5122 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 5123 | if (!presContext) { |
michael@0 | 5124 | return 1.0; |
michael@0 | 5125 | } |
michael@0 | 5126 | |
michael@0 | 5127 | return float(nsPresContext::AppUnitsPerCSSPixel())/ |
michael@0 | 5128 | presContext->AppUnitsPerDevPixel(); |
michael@0 | 5129 | } |
michael@0 | 5130 | |
michael@0 | 5131 | NS_IMETHODIMP |
michael@0 | 5132 | nsGlobalWindow::GetDevicePixelRatio(float* aRatio) |
michael@0 | 5133 | { |
michael@0 | 5134 | ErrorResult rv; |
michael@0 | 5135 | *aRatio = GetDevicePixelRatio(rv); |
michael@0 | 5136 | |
michael@0 | 5137 | return rv.ErrorCode(); |
michael@0 | 5138 | } |
michael@0 | 5139 | |
michael@0 | 5140 | uint64_t |
michael@0 | 5141 | nsGlobalWindow::GetMozPaintCount(ErrorResult& aError) |
michael@0 | 5142 | { |
michael@0 | 5143 | FORWARD_TO_OUTER_OR_THROW(GetMozPaintCount, (aError), aError, 0); |
michael@0 | 5144 | |
michael@0 | 5145 | if (!mDocShell) { |
michael@0 | 5146 | return 0; |
michael@0 | 5147 | } |
michael@0 | 5148 | |
michael@0 | 5149 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 5150 | return presShell ? presShell->GetPaintCount() : 0; |
michael@0 | 5151 | } |
michael@0 | 5152 | |
michael@0 | 5153 | NS_IMETHODIMP |
michael@0 | 5154 | nsGlobalWindow::GetMozPaintCount(uint64_t* aResult) |
michael@0 | 5155 | { |
michael@0 | 5156 | ErrorResult rv; |
michael@0 | 5157 | *aResult = GetMozPaintCount(rv); |
michael@0 | 5158 | |
michael@0 | 5159 | return rv.ErrorCode(); |
michael@0 | 5160 | } |
michael@0 | 5161 | |
michael@0 | 5162 | NS_IMETHODIMP |
michael@0 | 5163 | nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback, |
michael@0 | 5164 | int32_t *aHandle) |
michael@0 | 5165 | { |
michael@0 | 5166 | if (!aCallback) { |
michael@0 | 5167 | if (mDoc) { |
michael@0 | 5168 | mDoc->WarnOnceAbout(nsIDocument::eMozBeforePaint); |
michael@0 | 5169 | } |
michael@0 | 5170 | return NS_ERROR_XPC_BAD_CONVERT_JS; |
michael@0 | 5171 | } |
michael@0 | 5172 | |
michael@0 | 5173 | ErrorResult rv; |
michael@0 | 5174 | nsIDocument::FrameRequestCallbackHolder holder(aCallback); |
michael@0 | 5175 | *aHandle = RequestAnimationFrame(holder, rv); |
michael@0 | 5176 | |
michael@0 | 5177 | return rv.ErrorCode(); |
michael@0 | 5178 | } |
michael@0 | 5179 | |
michael@0 | 5180 | int32_t |
michael@0 | 5181 | nsGlobalWindow::RequestAnimationFrame(FrameRequestCallback& aCallback, |
michael@0 | 5182 | ErrorResult& aError) |
michael@0 | 5183 | { |
michael@0 | 5184 | nsIDocument::FrameRequestCallbackHolder holder(&aCallback); |
michael@0 | 5185 | return RequestAnimationFrame(holder, aError); |
michael@0 | 5186 | } |
michael@0 | 5187 | |
michael@0 | 5188 | int32_t |
michael@0 | 5189 | nsGlobalWindow::MozRequestAnimationFrame(nsIFrameRequestCallback* aCallback, |
michael@0 | 5190 | ErrorResult& aError) |
michael@0 | 5191 | { |
michael@0 | 5192 | nsIDocument::FrameRequestCallbackHolder holder(aCallback); |
michael@0 | 5193 | return RequestAnimationFrame(holder, aError); |
michael@0 | 5194 | } |
michael@0 | 5195 | |
michael@0 | 5196 | int32_t |
michael@0 | 5197 | nsGlobalWindow::RequestAnimationFrame(const nsIDocument::FrameRequestCallbackHolder& aCallback, |
michael@0 | 5198 | ErrorResult& aError) |
michael@0 | 5199 | { |
michael@0 | 5200 | FORWARD_TO_INNER_OR_THROW(RequestAnimationFrame, (aCallback, aError), aError, |
michael@0 | 5201 | 0); |
michael@0 | 5202 | |
michael@0 | 5203 | if (!mDoc) { |
michael@0 | 5204 | return 0; |
michael@0 | 5205 | } |
michael@0 | 5206 | |
michael@0 | 5207 | if (GetWrapperPreserveColor()) { |
michael@0 | 5208 | js::NotifyAnimationActivity(GetWrapperPreserveColor()); |
michael@0 | 5209 | } |
michael@0 | 5210 | |
michael@0 | 5211 | int32_t handle; |
michael@0 | 5212 | aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle); |
michael@0 | 5213 | return handle; |
michael@0 | 5214 | } |
michael@0 | 5215 | |
michael@0 | 5216 | NS_IMETHODIMP |
michael@0 | 5217 | nsGlobalWindow::RequestAnimationFrame(JS::Handle<JS::Value> aCallback, |
michael@0 | 5218 | JSContext* cx, |
michael@0 | 5219 | int32_t* aHandle) |
michael@0 | 5220 | { |
michael@0 | 5221 | if (!aCallback.isObject() || !JS_ObjectIsCallable(cx, &aCallback.toObject())) { |
michael@0 | 5222 | return NS_ERROR_INVALID_ARG; |
michael@0 | 5223 | } |
michael@0 | 5224 | |
michael@0 | 5225 | JS::Rooted<JSObject*> callbackObj(cx, &aCallback.toObject()); |
michael@0 | 5226 | nsRefPtr<FrameRequestCallback> callback = |
michael@0 | 5227 | new FrameRequestCallback(callbackObj, GetIncumbentGlobal()); |
michael@0 | 5228 | |
michael@0 | 5229 | ErrorResult rv; |
michael@0 | 5230 | *aHandle = RequestAnimationFrame(*callback, rv); |
michael@0 | 5231 | |
michael@0 | 5232 | return rv.ErrorCode(); |
michael@0 | 5233 | } |
michael@0 | 5234 | |
michael@0 | 5235 | NS_IMETHODIMP |
michael@0 | 5236 | nsGlobalWindow::MozCancelRequestAnimationFrame(int32_t aHandle) |
michael@0 | 5237 | { |
michael@0 | 5238 | return CancelAnimationFrame(aHandle); |
michael@0 | 5239 | } |
michael@0 | 5240 | |
michael@0 | 5241 | NS_IMETHODIMP |
michael@0 | 5242 | nsGlobalWindow::MozCancelAnimationFrame(int32_t aHandle) |
michael@0 | 5243 | { |
michael@0 | 5244 | return CancelAnimationFrame(aHandle); |
michael@0 | 5245 | } |
michael@0 | 5246 | |
michael@0 | 5247 | void |
michael@0 | 5248 | nsGlobalWindow::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) |
michael@0 | 5249 | { |
michael@0 | 5250 | FORWARD_TO_INNER_OR_THROW(CancelAnimationFrame, (aHandle, aError), aError, ); |
michael@0 | 5251 | |
michael@0 | 5252 | if (!mDoc) { |
michael@0 | 5253 | return; |
michael@0 | 5254 | } |
michael@0 | 5255 | |
michael@0 | 5256 | mDoc->CancelFrameRequestCallback(aHandle); |
michael@0 | 5257 | } |
michael@0 | 5258 | |
michael@0 | 5259 | NS_IMETHODIMP |
michael@0 | 5260 | nsGlobalWindow::CancelAnimationFrame(int32_t aHandle) |
michael@0 | 5261 | { |
michael@0 | 5262 | ErrorResult rv; |
michael@0 | 5263 | CancelAnimationFrame(aHandle, rv); |
michael@0 | 5264 | |
michael@0 | 5265 | return rv.ErrorCode(); |
michael@0 | 5266 | } |
michael@0 | 5267 | |
michael@0 | 5268 | int64_t |
michael@0 | 5269 | nsGlobalWindow::GetMozAnimationStartTime(ErrorResult& aError) |
michael@0 | 5270 | { |
michael@0 | 5271 | FORWARD_TO_INNER_OR_THROW(GetMozAnimationStartTime, (aError), aError, 0); |
michael@0 | 5272 | |
michael@0 | 5273 | if (mDoc) { |
michael@0 | 5274 | nsIPresShell* presShell = mDoc->GetShell(); |
michael@0 | 5275 | if (presShell) { |
michael@0 | 5276 | return presShell->GetPresContext()->RefreshDriver()-> |
michael@0 | 5277 | MostRecentRefreshEpochTime() / PR_USEC_PER_MSEC; |
michael@0 | 5278 | } |
michael@0 | 5279 | } |
michael@0 | 5280 | |
michael@0 | 5281 | // If all else fails, just be compatible with Date.now() |
michael@0 | 5282 | return JS_Now() / PR_USEC_PER_MSEC; |
michael@0 | 5283 | } |
michael@0 | 5284 | |
michael@0 | 5285 | NS_IMETHODIMP |
michael@0 | 5286 | nsGlobalWindow::GetMozAnimationStartTime(int64_t *aTime) |
michael@0 | 5287 | { |
michael@0 | 5288 | ErrorResult rv; |
michael@0 | 5289 | *aTime = GetMozAnimationStartTime(rv); |
michael@0 | 5290 | |
michael@0 | 5291 | return rv.ErrorCode(); |
michael@0 | 5292 | } |
michael@0 | 5293 | |
michael@0 | 5294 | already_AddRefed<MediaQueryList> |
michael@0 | 5295 | nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList, |
michael@0 | 5296 | ErrorResult& aError) |
michael@0 | 5297 | { |
michael@0 | 5298 | // FIXME: This whole forward-to-outer and then get a pres |
michael@0 | 5299 | // shell/context off the docshell dance is sort of silly; it'd make |
michael@0 | 5300 | // more sense to forward to the inner, but it's what everyone else |
michael@0 | 5301 | // (GetSelection, GetScrollXY, etc.) does around here. |
michael@0 | 5302 | FORWARD_TO_OUTER_OR_THROW(MatchMedia, (aMediaQueryList, aError), aError, |
michael@0 | 5303 | nullptr); |
michael@0 | 5304 | |
michael@0 | 5305 | // We need this now to ensure that we have a non-null |presContext| |
michael@0 | 5306 | // when we ought to. |
michael@0 | 5307 | // This is similar to EnsureSizeUpToDate, but only flushes frames. |
michael@0 | 5308 | nsGlobalWindow *parent = static_cast<nsGlobalWindow*>(GetPrivateParent()); |
michael@0 | 5309 | if (parent) { |
michael@0 | 5310 | parent->FlushPendingNotifications(Flush_Frames); |
michael@0 | 5311 | } |
michael@0 | 5312 | |
michael@0 | 5313 | if (!mDocShell) { |
michael@0 | 5314 | return nullptr; |
michael@0 | 5315 | } |
michael@0 | 5316 | |
michael@0 | 5317 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 5318 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 5319 | |
michael@0 | 5320 | if (!presContext) { |
michael@0 | 5321 | return nullptr; |
michael@0 | 5322 | } |
michael@0 | 5323 | |
michael@0 | 5324 | return presContext->MatchMedia(aMediaQueryList); |
michael@0 | 5325 | } |
michael@0 | 5326 | |
michael@0 | 5327 | NS_IMETHODIMP |
michael@0 | 5328 | nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList, |
michael@0 | 5329 | nsISupports** aResult) |
michael@0 | 5330 | { |
michael@0 | 5331 | ErrorResult rv; |
michael@0 | 5332 | nsRefPtr<MediaQueryList> mediaQueryList = MatchMedia(aMediaQueryList, rv); |
michael@0 | 5333 | mediaQueryList.forget(aResult); |
michael@0 | 5334 | |
michael@0 | 5335 | return rv.ErrorCode(); |
michael@0 | 5336 | } |
michael@0 | 5337 | |
michael@0 | 5338 | void |
michael@0 | 5339 | nsGlobalWindow::SetScreenX(int32_t aScreenX, ErrorResult& aError) |
michael@0 | 5340 | { |
michael@0 | 5341 | FORWARD_TO_OUTER_OR_THROW(SetScreenX, (aScreenX, aError), aError, ); |
michael@0 | 5342 | |
michael@0 | 5343 | /* |
michael@0 | 5344 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 5345 | * prevent setting window.screenX by exiting early |
michael@0 | 5346 | */ |
michael@0 | 5347 | |
michael@0 | 5348 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 5349 | return; |
michael@0 | 5350 | } |
michael@0 | 5351 | |
michael@0 | 5352 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 5353 | if (!treeOwnerAsWin) { |
michael@0 | 5354 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 5355 | return; |
michael@0 | 5356 | } |
michael@0 | 5357 | |
michael@0 | 5358 | int32_t x, y; |
michael@0 | 5359 | aError = treeOwnerAsWin->GetPosition(&x, &y); |
michael@0 | 5360 | if (aError.Failed()) { |
michael@0 | 5361 | return; |
michael@0 | 5362 | } |
michael@0 | 5363 | |
michael@0 | 5364 | CheckSecurityLeftAndTop(&aScreenX, nullptr); |
michael@0 | 5365 | x = CSSToDevIntPixels(aScreenX); |
michael@0 | 5366 | |
michael@0 | 5367 | aError = treeOwnerAsWin->SetPosition(x, y); |
michael@0 | 5368 | } |
michael@0 | 5369 | |
michael@0 | 5370 | NS_IMETHODIMP |
michael@0 | 5371 | nsGlobalWindow::SetScreenX(int32_t aScreenX) |
michael@0 | 5372 | { |
michael@0 | 5373 | ErrorResult rv; |
michael@0 | 5374 | SetScreenX(aScreenX, rv); |
michael@0 | 5375 | |
michael@0 | 5376 | return rv.ErrorCode(); |
michael@0 | 5377 | } |
michael@0 | 5378 | |
michael@0 | 5379 | int32_t |
michael@0 | 5380 | nsGlobalWindow::GetScreenY(ErrorResult& aError) |
michael@0 | 5381 | { |
michael@0 | 5382 | FORWARD_TO_OUTER_OR_THROW(GetScreenY, (aError), aError, 0); |
michael@0 | 5383 | |
michael@0 | 5384 | return DevToCSSIntPixels(GetScreenXY(aError).y); |
michael@0 | 5385 | } |
michael@0 | 5386 | |
michael@0 | 5387 | NS_IMETHODIMP |
michael@0 | 5388 | nsGlobalWindow::GetScreenY(int32_t* aScreenY) |
michael@0 | 5389 | { |
michael@0 | 5390 | ErrorResult rv; |
michael@0 | 5391 | *aScreenY = GetScreenY(rv); |
michael@0 | 5392 | |
michael@0 | 5393 | return rv.ErrorCode(); |
michael@0 | 5394 | } |
michael@0 | 5395 | |
michael@0 | 5396 | void |
michael@0 | 5397 | nsGlobalWindow::SetScreenY(int32_t aScreenY, ErrorResult& aError) |
michael@0 | 5398 | { |
michael@0 | 5399 | FORWARD_TO_OUTER_OR_THROW(SetScreenY, (aScreenY, aError), aError, ); |
michael@0 | 5400 | |
michael@0 | 5401 | /* |
michael@0 | 5402 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 5403 | * prevent setting window.screenY by exiting early |
michael@0 | 5404 | */ |
michael@0 | 5405 | |
michael@0 | 5406 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 5407 | return; |
michael@0 | 5408 | } |
michael@0 | 5409 | |
michael@0 | 5410 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 5411 | if (!treeOwnerAsWin) { |
michael@0 | 5412 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 5413 | return; |
michael@0 | 5414 | } |
michael@0 | 5415 | |
michael@0 | 5416 | int32_t x, y; |
michael@0 | 5417 | aError = treeOwnerAsWin->GetPosition(&x, &y); |
michael@0 | 5418 | if (aError.Failed()) { |
michael@0 | 5419 | return; |
michael@0 | 5420 | } |
michael@0 | 5421 | |
michael@0 | 5422 | CheckSecurityLeftAndTop(nullptr, &aScreenY); |
michael@0 | 5423 | y = CSSToDevIntPixels(aScreenY); |
michael@0 | 5424 | |
michael@0 | 5425 | aError = treeOwnerAsWin->SetPosition(x, y); |
michael@0 | 5426 | } |
michael@0 | 5427 | |
michael@0 | 5428 | NS_IMETHODIMP |
michael@0 | 5429 | nsGlobalWindow::SetScreenY(int32_t aScreenY) |
michael@0 | 5430 | { |
michael@0 | 5431 | ErrorResult rv; |
michael@0 | 5432 | SetScreenY(aScreenY, rv); |
michael@0 | 5433 | |
michael@0 | 5434 | return rv.ErrorCode(); |
michael@0 | 5435 | } |
michael@0 | 5436 | |
michael@0 | 5437 | bool |
michael@0 | 5438 | nsGlobalWindow::IsChrome() const |
michael@0 | 5439 | { |
michael@0 | 5440 | bool isChrome = false; |
michael@0 | 5441 | |
michael@0 | 5442 | if (mDocShell) { |
michael@0 | 5443 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 5444 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 5445 | isChrome = (presContext && presContext->IsChrome()); |
michael@0 | 5446 | } |
michael@0 | 5447 | |
michael@0 | 5448 | return isChrome; |
michael@0 | 5449 | } |
michael@0 | 5450 | |
michael@0 | 5451 | // NOTE: Arguments to this function should have values scaled to |
michael@0 | 5452 | // CSS pixels, not device pixels. |
michael@0 | 5453 | void |
michael@0 | 5454 | nsGlobalWindow::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight) |
michael@0 | 5455 | { |
michael@0 | 5456 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 5457 | |
michael@0 | 5458 | #ifdef MOZ_XUL |
michael@0 | 5459 | if (!nsContentUtils::IsCallerChrome()) { |
michael@0 | 5460 | // if attempting to resize the window, hide any open popups |
michael@0 | 5461 | nsContentUtils::HidePopupsInDocument(mDoc); |
michael@0 | 5462 | } |
michael@0 | 5463 | #endif |
michael@0 | 5464 | |
michael@0 | 5465 | // This one is easy. Just ensure the variable is greater than 100; |
michael@0 | 5466 | if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) { |
michael@0 | 5467 | // Check security state for use in determing window dimensions |
michael@0 | 5468 | |
michael@0 | 5469 | if (!nsContentUtils::IsCallerChrome()) { |
michael@0 | 5470 | //sec check failed |
michael@0 | 5471 | if (aWidth && *aWidth < 100) { |
michael@0 | 5472 | *aWidth = 100; |
michael@0 | 5473 | } |
michael@0 | 5474 | if (aHeight && *aHeight < 100) { |
michael@0 | 5475 | *aHeight = 100; |
michael@0 | 5476 | } |
michael@0 | 5477 | } |
michael@0 | 5478 | } |
michael@0 | 5479 | } |
michael@0 | 5480 | |
michael@0 | 5481 | // NOTE: Arguments to this function should have values in device pixels |
michael@0 | 5482 | nsresult |
michael@0 | 5483 | nsGlobalWindow::SetDocShellWidthAndHeight(int32_t aInnerWidth, int32_t aInnerHeight) |
michael@0 | 5484 | { |
michael@0 | 5485 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 5486 | |
michael@0 | 5487 | NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); |
michael@0 | 5488 | |
michael@0 | 5489 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner; |
michael@0 | 5490 | mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); |
michael@0 | 5491 | NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE); |
michael@0 | 5492 | |
michael@0 | 5493 | NS_ENSURE_SUCCESS(treeOwner->SizeShellTo(mDocShell, aInnerWidth, aInnerHeight), |
michael@0 | 5494 | NS_ERROR_FAILURE); |
michael@0 | 5495 | |
michael@0 | 5496 | return NS_OK; |
michael@0 | 5497 | } |
michael@0 | 5498 | |
michael@0 | 5499 | // NOTE: Arguments to this function should have values in app units |
michael@0 | 5500 | void |
michael@0 | 5501 | nsGlobalWindow::SetCSSViewportWidthAndHeight(nscoord aInnerWidth, nscoord aInnerHeight) |
michael@0 | 5502 | { |
michael@0 | 5503 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 5504 | |
michael@0 | 5505 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 5506 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 5507 | |
michael@0 | 5508 | nsRect shellArea = presContext->GetVisibleArea(); |
michael@0 | 5509 | shellArea.height = aInnerHeight; |
michael@0 | 5510 | shellArea.width = aInnerWidth; |
michael@0 | 5511 | |
michael@0 | 5512 | presContext->SetVisibleArea(shellArea); |
michael@0 | 5513 | } |
michael@0 | 5514 | |
michael@0 | 5515 | // NOTE: Arguments to this function should have values scaled to |
michael@0 | 5516 | // CSS pixels, not device pixels. |
michael@0 | 5517 | void |
michael@0 | 5518 | nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop) |
michael@0 | 5519 | { |
michael@0 | 5520 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 5521 | |
michael@0 | 5522 | // This one is harder. We have to get the screen size and window dimensions. |
michael@0 | 5523 | |
michael@0 | 5524 | // Check security state for use in determing window dimensions |
michael@0 | 5525 | |
michael@0 | 5526 | if (!nsContentUtils::IsCallerChrome()) { |
michael@0 | 5527 | #ifdef MOZ_XUL |
michael@0 | 5528 | // if attempting to move the window, hide any open popups |
michael@0 | 5529 | nsContentUtils::HidePopupsInDocument(mDoc); |
michael@0 | 5530 | #endif |
michael@0 | 5531 | |
michael@0 | 5532 | nsGlobalWindow* rootWindow = |
michael@0 | 5533 | static_cast<nsGlobalWindow*>(GetPrivateRoot()); |
michael@0 | 5534 | if (rootWindow) { |
michael@0 | 5535 | rootWindow->FlushPendingNotifications(Flush_Layout); |
michael@0 | 5536 | } |
michael@0 | 5537 | |
michael@0 | 5538 | nsCOMPtr<nsIBaseWindow> treeOwner = GetTreeOwnerWindow(); |
michael@0 | 5539 | |
michael@0 | 5540 | nsCOMPtr<nsIDOMScreen> screen; |
michael@0 | 5541 | GetScreen(getter_AddRefs(screen)); |
michael@0 | 5542 | |
michael@0 | 5543 | if (treeOwner && screen) { |
michael@0 | 5544 | int32_t screenLeft, screenTop, screenWidth, screenHeight; |
michael@0 | 5545 | int32_t winLeft, winTop, winWidth, winHeight; |
michael@0 | 5546 | |
michael@0 | 5547 | // Get the window size |
michael@0 | 5548 | treeOwner->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight); |
michael@0 | 5549 | |
michael@0 | 5550 | // convert those values to CSS pixels |
michael@0 | 5551 | // XXX four separate retrievals of the prescontext |
michael@0 | 5552 | winLeft = DevToCSSIntPixels(winLeft); |
michael@0 | 5553 | winTop = DevToCSSIntPixels(winTop); |
michael@0 | 5554 | winWidth = DevToCSSIntPixels(winWidth); |
michael@0 | 5555 | winHeight = DevToCSSIntPixels(winHeight); |
michael@0 | 5556 | |
michael@0 | 5557 | // Get the screen dimensions |
michael@0 | 5558 | // XXX This should use nsIScreenManager once it's fully fleshed out. |
michael@0 | 5559 | screen->GetAvailLeft(&screenLeft); |
michael@0 | 5560 | screen->GetAvailWidth(&screenWidth); |
michael@0 | 5561 | screen->GetAvailHeight(&screenHeight); |
michael@0 | 5562 | #if defined(XP_MACOSX) |
michael@0 | 5563 | /* The mac's coordinate system is different from the assumed Windows' |
michael@0 | 5564 | system. It offsets by the height of the menubar so that a window |
michael@0 | 5565 | placed at (0,0) will be entirely visible. Unfortunately that |
michael@0 | 5566 | correction is made elsewhere (in Widget) and the meaning of |
michael@0 | 5567 | the Avail... coordinates is overloaded. Here we allow a window |
michael@0 | 5568 | to be placed at (0,0) because it does make sense to do so. |
michael@0 | 5569 | */ |
michael@0 | 5570 | screen->GetTop(&screenTop); |
michael@0 | 5571 | #else |
michael@0 | 5572 | screen->GetAvailTop(&screenTop); |
michael@0 | 5573 | #endif |
michael@0 | 5574 | |
michael@0 | 5575 | if (aLeft) { |
michael@0 | 5576 | if (screenLeft+screenWidth < *aLeft+winWidth) |
michael@0 | 5577 | *aLeft = screenLeft+screenWidth - winWidth; |
michael@0 | 5578 | if (screenLeft > *aLeft) |
michael@0 | 5579 | *aLeft = screenLeft; |
michael@0 | 5580 | } |
michael@0 | 5581 | if (aTop) { |
michael@0 | 5582 | if (screenTop+screenHeight < *aTop+winHeight) |
michael@0 | 5583 | *aTop = screenTop+screenHeight - winHeight; |
michael@0 | 5584 | if (screenTop > *aTop) |
michael@0 | 5585 | *aTop = screenTop; |
michael@0 | 5586 | } |
michael@0 | 5587 | } else { |
michael@0 | 5588 | if (aLeft) |
michael@0 | 5589 | *aLeft = 0; |
michael@0 | 5590 | if (aTop) |
michael@0 | 5591 | *aTop = 0; |
michael@0 | 5592 | } |
michael@0 | 5593 | } |
michael@0 | 5594 | } |
michael@0 | 5595 | |
michael@0 | 5596 | NS_IMETHODIMP |
michael@0 | 5597 | nsGlobalWindow::GetPageXOffset(int32_t* aPageXOffset) |
michael@0 | 5598 | { |
michael@0 | 5599 | return GetScrollX(aPageXOffset); |
michael@0 | 5600 | } |
michael@0 | 5601 | |
michael@0 | 5602 | NS_IMETHODIMP |
michael@0 | 5603 | nsGlobalWindow::GetPageYOffset(int32_t* aPageYOffset) |
michael@0 | 5604 | { |
michael@0 | 5605 | return GetScrollY(aPageYOffset); |
michael@0 | 5606 | } |
michael@0 | 5607 | |
michael@0 | 5608 | void |
michael@0 | 5609 | nsGlobalWindow::GetScrollMaxXY(int32_t* aScrollMaxX, int32_t* aScrollMaxY, |
michael@0 | 5610 | ErrorResult& aError) |
michael@0 | 5611 | { |
michael@0 | 5612 | FORWARD_TO_OUTER_OR_THROW(GetScrollMaxXY, (aScrollMaxX, aScrollMaxY, aError), |
michael@0 | 5613 | aError, ); |
michael@0 | 5614 | |
michael@0 | 5615 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 5616 | nsIScrollableFrame *sf = GetScrollFrame(); |
michael@0 | 5617 | if (!sf) { |
michael@0 | 5618 | return; |
michael@0 | 5619 | } |
michael@0 | 5620 | |
michael@0 | 5621 | nsRect scrollRange = sf->GetScrollRange(); |
michael@0 | 5622 | |
michael@0 | 5623 | if (aScrollMaxX) { |
michael@0 | 5624 | *aScrollMaxX = std::max(0, |
michael@0 | 5625 | (int32_t)floor(nsPresContext::AppUnitsToFloatCSSPixels(scrollRange.XMost()))); |
michael@0 | 5626 | } |
michael@0 | 5627 | if (aScrollMaxY) { |
michael@0 | 5628 | *aScrollMaxY = std::max(0, |
michael@0 | 5629 | (int32_t)floor(nsPresContext::AppUnitsToFloatCSSPixels(scrollRange.YMost()))); |
michael@0 | 5630 | } |
michael@0 | 5631 | } |
michael@0 | 5632 | |
michael@0 | 5633 | int32_t |
michael@0 | 5634 | nsGlobalWindow::GetScrollMaxX(ErrorResult& aError) |
michael@0 | 5635 | { |
michael@0 | 5636 | int32_t scrollMaxX = 0; |
michael@0 | 5637 | GetScrollMaxXY(&scrollMaxX, nullptr, aError); |
michael@0 | 5638 | return scrollMaxX; |
michael@0 | 5639 | } |
michael@0 | 5640 | |
michael@0 | 5641 | NS_IMETHODIMP |
michael@0 | 5642 | nsGlobalWindow::GetScrollMaxX(int32_t* aScrollMaxX) |
michael@0 | 5643 | { |
michael@0 | 5644 | NS_ENSURE_ARG_POINTER(aScrollMaxX); |
michael@0 | 5645 | ErrorResult rv; |
michael@0 | 5646 | *aScrollMaxX = GetScrollMaxX(rv); |
michael@0 | 5647 | |
michael@0 | 5648 | return rv.ErrorCode(); |
michael@0 | 5649 | } |
michael@0 | 5650 | |
michael@0 | 5651 | int32_t |
michael@0 | 5652 | nsGlobalWindow::GetScrollMaxY(ErrorResult& aError) |
michael@0 | 5653 | { |
michael@0 | 5654 | int32_t scrollMaxY = 0; |
michael@0 | 5655 | GetScrollMaxXY(nullptr, &scrollMaxY, aError); |
michael@0 | 5656 | return scrollMaxY; |
michael@0 | 5657 | } |
michael@0 | 5658 | |
michael@0 | 5659 | NS_IMETHODIMP |
michael@0 | 5660 | nsGlobalWindow::GetScrollMaxY(int32_t* aScrollMaxY) |
michael@0 | 5661 | { |
michael@0 | 5662 | NS_ENSURE_ARG_POINTER(aScrollMaxY); |
michael@0 | 5663 | ErrorResult rv; |
michael@0 | 5664 | *aScrollMaxY = GetScrollMaxY(rv); |
michael@0 | 5665 | |
michael@0 | 5666 | return rv.ErrorCode(); |
michael@0 | 5667 | } |
michael@0 | 5668 | |
michael@0 | 5669 | CSSIntPoint |
michael@0 | 5670 | nsGlobalWindow::GetScrollXY(bool aDoFlush, ErrorResult& aError) |
michael@0 | 5671 | { |
michael@0 | 5672 | FORWARD_TO_OUTER_OR_THROW(GetScrollXY, (aDoFlush, aError), aError, |
michael@0 | 5673 | CSSIntPoint(0, 0)); |
michael@0 | 5674 | |
michael@0 | 5675 | if (aDoFlush) { |
michael@0 | 5676 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 5677 | } else { |
michael@0 | 5678 | EnsureSizeUpToDate(); |
michael@0 | 5679 | } |
michael@0 | 5680 | |
michael@0 | 5681 | nsIScrollableFrame *sf = GetScrollFrame(); |
michael@0 | 5682 | if (!sf) { |
michael@0 | 5683 | return CSSIntPoint(0, 0); |
michael@0 | 5684 | } |
michael@0 | 5685 | |
michael@0 | 5686 | nsPoint scrollPos = sf->GetScrollPosition(); |
michael@0 | 5687 | if (scrollPos != nsPoint(0,0) && !aDoFlush) { |
michael@0 | 5688 | // Oh, well. This is the expensive case -- the window is scrolled and we |
michael@0 | 5689 | // didn't actually flush yet. Repeat, but with a flush, since the content |
michael@0 | 5690 | // may get shorter and hence our scroll position may decrease. |
michael@0 | 5691 | return GetScrollXY(true, aError); |
michael@0 | 5692 | } |
michael@0 | 5693 | |
michael@0 | 5694 | return sf->GetScrollPositionCSSPixels(); |
michael@0 | 5695 | } |
michael@0 | 5696 | |
michael@0 | 5697 | int32_t |
michael@0 | 5698 | nsGlobalWindow::GetScrollX(ErrorResult& aError) |
michael@0 | 5699 | { |
michael@0 | 5700 | return GetScrollXY(false, aError).x; |
michael@0 | 5701 | } |
michael@0 | 5702 | |
michael@0 | 5703 | NS_IMETHODIMP |
michael@0 | 5704 | nsGlobalWindow::GetScrollX(int32_t* aScrollX) |
michael@0 | 5705 | { |
michael@0 | 5706 | NS_ENSURE_ARG_POINTER(aScrollX); |
michael@0 | 5707 | ErrorResult rv; |
michael@0 | 5708 | *aScrollX = GetScrollXY(false, rv).x; |
michael@0 | 5709 | return rv.ErrorCode(); |
michael@0 | 5710 | } |
michael@0 | 5711 | |
michael@0 | 5712 | int32_t |
michael@0 | 5713 | nsGlobalWindow::GetScrollY(ErrorResult& aError) |
michael@0 | 5714 | { |
michael@0 | 5715 | return GetScrollXY(false, aError).y; |
michael@0 | 5716 | } |
michael@0 | 5717 | |
michael@0 | 5718 | NS_IMETHODIMP |
michael@0 | 5719 | nsGlobalWindow::GetScrollY(int32_t* aScrollY) |
michael@0 | 5720 | { |
michael@0 | 5721 | NS_ENSURE_ARG_POINTER(aScrollY); |
michael@0 | 5722 | ErrorResult rv; |
michael@0 | 5723 | *aScrollY = GetScrollXY(false, rv).y; |
michael@0 | 5724 | return rv.ErrorCode(); |
michael@0 | 5725 | } |
michael@0 | 5726 | |
michael@0 | 5727 | uint32_t |
michael@0 | 5728 | nsGlobalWindow::Length() |
michael@0 | 5729 | { |
michael@0 | 5730 | FORWARD_TO_OUTER(Length, (), 0); |
michael@0 | 5731 | |
michael@0 | 5732 | nsDOMWindowList* windows = GetWindowList(); |
michael@0 | 5733 | |
michael@0 | 5734 | return windows ? windows->GetLength() : 0; |
michael@0 | 5735 | } |
michael@0 | 5736 | |
michael@0 | 5737 | NS_IMETHODIMP |
michael@0 | 5738 | nsGlobalWindow::GetLength(uint32_t* aLength) |
michael@0 | 5739 | { |
michael@0 | 5740 | *aLength = Length(); |
michael@0 | 5741 | return NS_OK; |
michael@0 | 5742 | } |
michael@0 | 5743 | |
michael@0 | 5744 | already_AddRefed<nsIDOMWindow> |
michael@0 | 5745 | nsGlobalWindow::GetChildWindow(const nsAString& aName) |
michael@0 | 5746 | { |
michael@0 | 5747 | nsCOMPtr<nsIDocShell> docShell(GetDocShell()); |
michael@0 | 5748 | NS_ENSURE_TRUE(docShell, nullptr); |
michael@0 | 5749 | |
michael@0 | 5750 | nsCOMPtr<nsIDocShellTreeItem> child; |
michael@0 | 5751 | docShell->FindChildWithName(PromiseFlatString(aName).get(), |
michael@0 | 5752 | false, true, nullptr, nullptr, |
michael@0 | 5753 | getter_AddRefs(child)); |
michael@0 | 5754 | |
michael@0 | 5755 | nsCOMPtr<nsIDOMWindow> child_win(do_GetInterface(child)); |
michael@0 | 5756 | return child_win.forget(); |
michael@0 | 5757 | } |
michael@0 | 5758 | |
michael@0 | 5759 | bool |
michael@0 | 5760 | nsGlobalWindow::DispatchCustomEvent(const char *aEventName) |
michael@0 | 5761 | { |
michael@0 | 5762 | bool defaultActionEnabled = true; |
michael@0 | 5763 | nsContentUtils::DispatchTrustedEvent(mDoc, |
michael@0 | 5764 | GetOuterWindow(), |
michael@0 | 5765 | NS_ConvertASCIItoUTF16(aEventName), |
michael@0 | 5766 | true, true, &defaultActionEnabled); |
michael@0 | 5767 | |
michael@0 | 5768 | return defaultActionEnabled; |
michael@0 | 5769 | } |
michael@0 | 5770 | |
michael@0 | 5771 | // NOTE: Arguments to this function should be CSS pixels, not device pixels. |
michael@0 | 5772 | bool |
michael@0 | 5773 | nsGlobalWindow::DispatchResizeEvent(const nsIntSize& aSize) |
michael@0 | 5774 | { |
michael@0 | 5775 | ErrorResult res; |
michael@0 | 5776 | nsRefPtr<Event> domEvent = |
michael@0 | 5777 | mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res); |
michael@0 | 5778 | if (res.Failed()) { |
michael@0 | 5779 | return false; |
michael@0 | 5780 | } |
michael@0 | 5781 | |
michael@0 | 5782 | AutoSafeJSContext cx; |
michael@0 | 5783 | JSAutoCompartment ac(cx, GetWrapperPreserveColor()); |
michael@0 | 5784 | DOMWindowResizeEventDetail detail; |
michael@0 | 5785 | detail.mWidth = aSize.width; |
michael@0 | 5786 | detail.mHeight = aSize.height; |
michael@0 | 5787 | JS::Rooted<JS::Value> detailValue(cx); |
michael@0 | 5788 | if (!detail.ToObject(cx, &detailValue)) { |
michael@0 | 5789 | return false; |
michael@0 | 5790 | } |
michael@0 | 5791 | |
michael@0 | 5792 | CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get()); |
michael@0 | 5793 | customEvent->InitCustomEvent(cx, |
michael@0 | 5794 | NS_LITERAL_STRING("DOMWindowResize"), |
michael@0 | 5795 | /* bubbles = */ true, |
michael@0 | 5796 | /* cancelable = */ true, |
michael@0 | 5797 | detailValue, |
michael@0 | 5798 | res); |
michael@0 | 5799 | if (res.Failed()) { |
michael@0 | 5800 | return false; |
michael@0 | 5801 | } |
michael@0 | 5802 | |
michael@0 | 5803 | domEvent->SetTrusted(true); |
michael@0 | 5804 | domEvent->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true; |
michael@0 | 5805 | |
michael@0 | 5806 | nsCOMPtr<EventTarget> target = do_QueryInterface(GetOuterWindow()); |
michael@0 | 5807 | domEvent->SetTarget(target); |
michael@0 | 5808 | |
michael@0 | 5809 | bool defaultActionEnabled = true; |
michael@0 | 5810 | target->DispatchEvent(domEvent, &defaultActionEnabled); |
michael@0 | 5811 | |
michael@0 | 5812 | return defaultActionEnabled; |
michael@0 | 5813 | } |
michael@0 | 5814 | |
michael@0 | 5815 | void |
michael@0 | 5816 | nsGlobalWindow::RefreshCompartmentPrincipal() |
michael@0 | 5817 | { |
michael@0 | 5818 | FORWARD_TO_INNER(RefreshCompartmentPrincipal, (), /* void */ ); |
michael@0 | 5819 | |
michael@0 | 5820 | JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()), |
michael@0 | 5821 | nsJSPrincipals::get(mDoc->NodePrincipal())); |
michael@0 | 5822 | } |
michael@0 | 5823 | |
michael@0 | 5824 | static already_AddRefed<nsIDocShellTreeItem> |
michael@0 | 5825 | GetCallerDocShellTreeItem() |
michael@0 | 5826 | { |
michael@0 | 5827 | JSContext *cx = nsContentUtils::GetCurrentJSContext(); |
michael@0 | 5828 | nsCOMPtr<nsIDocShellTreeItem> callerItem; |
michael@0 | 5829 | |
michael@0 | 5830 | if (cx) { |
michael@0 | 5831 | nsCOMPtr<nsIWebNavigation> callerWebNav = |
michael@0 | 5832 | do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx)); |
michael@0 | 5833 | |
michael@0 | 5834 | callerItem = do_QueryInterface(callerWebNav); |
michael@0 | 5835 | } |
michael@0 | 5836 | |
michael@0 | 5837 | return callerItem.forget(); |
michael@0 | 5838 | } |
michael@0 | 5839 | |
michael@0 | 5840 | bool |
michael@0 | 5841 | nsGlobalWindow::WindowExists(const nsAString& aName, |
michael@0 | 5842 | bool aLookForCallerOnJSStack) |
michael@0 | 5843 | { |
michael@0 | 5844 | NS_PRECONDITION(IsOuterWindow(), "Must be outer window"); |
michael@0 | 5845 | NS_PRECONDITION(mDocShell, "Must have docshell"); |
michael@0 | 5846 | |
michael@0 | 5847 | nsCOMPtr<nsIDocShellTreeItem> caller; |
michael@0 | 5848 | if (aLookForCallerOnJSStack) { |
michael@0 | 5849 | caller = GetCallerDocShellTreeItem(); |
michael@0 | 5850 | } |
michael@0 | 5851 | |
michael@0 | 5852 | if (!caller) { |
michael@0 | 5853 | caller = mDocShell; |
michael@0 | 5854 | } |
michael@0 | 5855 | |
michael@0 | 5856 | nsCOMPtr<nsIDocShellTreeItem> namedItem; |
michael@0 | 5857 | mDocShell->FindItemWithName(PromiseFlatString(aName).get(), nullptr, caller, |
michael@0 | 5858 | getter_AddRefs(namedItem)); |
michael@0 | 5859 | return namedItem != nullptr; |
michael@0 | 5860 | } |
michael@0 | 5861 | |
michael@0 | 5862 | already_AddRefed<nsIWidget> |
michael@0 | 5863 | nsGlobalWindow::GetMainWidget() |
michael@0 | 5864 | { |
michael@0 | 5865 | FORWARD_TO_OUTER(GetMainWidget, (), nullptr); |
michael@0 | 5866 | |
michael@0 | 5867 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 5868 | |
michael@0 | 5869 | nsCOMPtr<nsIWidget> widget; |
michael@0 | 5870 | |
michael@0 | 5871 | if (treeOwnerAsWin) { |
michael@0 | 5872 | treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget)); |
michael@0 | 5873 | } |
michael@0 | 5874 | |
michael@0 | 5875 | return widget.forget(); |
michael@0 | 5876 | } |
michael@0 | 5877 | |
michael@0 | 5878 | nsIWidget* |
michael@0 | 5879 | nsGlobalWindow::GetNearestWidget() |
michael@0 | 5880 | { |
michael@0 | 5881 | nsIDocShell* docShell = GetDocShell(); |
michael@0 | 5882 | NS_ENSURE_TRUE(docShell, nullptr); |
michael@0 | 5883 | nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); |
michael@0 | 5884 | NS_ENSURE_TRUE(presShell, nullptr); |
michael@0 | 5885 | nsIFrame* rootFrame = presShell->GetRootFrame(); |
michael@0 | 5886 | NS_ENSURE_TRUE(rootFrame, nullptr); |
michael@0 | 5887 | return rootFrame->GetView()->GetNearestWidget(nullptr); |
michael@0 | 5888 | } |
michael@0 | 5889 | |
michael@0 | 5890 | void |
michael@0 | 5891 | nsGlobalWindow::SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError) |
michael@0 | 5892 | { |
michael@0 | 5893 | aError = SetFullScreenInternal(aFullScreen, true); |
michael@0 | 5894 | } |
michael@0 | 5895 | |
michael@0 | 5896 | NS_IMETHODIMP |
michael@0 | 5897 | nsGlobalWindow::SetFullScreen(bool aFullScreen) |
michael@0 | 5898 | { |
michael@0 | 5899 | return SetFullScreenInternal(aFullScreen, true); |
michael@0 | 5900 | } |
michael@0 | 5901 | |
michael@0 | 5902 | nsresult |
michael@0 | 5903 | nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust) |
michael@0 | 5904 | { |
michael@0 | 5905 | FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 5906 | |
michael@0 | 5907 | NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); |
michael@0 | 5908 | |
michael@0 | 5909 | bool rootWinFullScreen; |
michael@0 | 5910 | GetFullScreen(&rootWinFullScreen); |
michael@0 | 5911 | // Only chrome can change our fullScreen mode, unless we're running in |
michael@0 | 5912 | // untrusted mode. |
michael@0 | 5913 | if (aFullScreen == rootWinFullScreen || |
michael@0 | 5914 | (aRequireTrust && !nsContentUtils::IsCallerChrome())) { |
michael@0 | 5915 | return NS_OK; |
michael@0 | 5916 | } |
michael@0 | 5917 | |
michael@0 | 5918 | // SetFullScreen needs to be called on the root window, so get that |
michael@0 | 5919 | // via the DocShell tree, and if we are not already the root, |
michael@0 | 5920 | // call SetFullScreen on that window instead. |
michael@0 | 5921 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
michael@0 | 5922 | mDocShell->GetRootTreeItem(getter_AddRefs(rootItem)); |
michael@0 | 5923 | nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(rootItem); |
michael@0 | 5924 | if (!window) |
michael@0 | 5925 | return NS_ERROR_FAILURE; |
michael@0 | 5926 | if (rootItem != mDocShell) |
michael@0 | 5927 | return window->SetFullScreenInternal(aFullScreen, aRequireTrust); |
michael@0 | 5928 | |
michael@0 | 5929 | // make sure we don't try to set full screen on a non-chrome window, |
michael@0 | 5930 | // which might happen in embedding world |
michael@0 | 5931 | if (mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) |
michael@0 | 5932 | return NS_ERROR_FAILURE; |
michael@0 | 5933 | |
michael@0 | 5934 | // If we are already in full screen mode, just return. |
michael@0 | 5935 | if (mFullScreen == aFullScreen) |
michael@0 | 5936 | return NS_OK; |
michael@0 | 5937 | |
michael@0 | 5938 | // dispatch a "fullscreen" DOM event so that XUL apps can |
michael@0 | 5939 | // respond visually if we are kicked into full screen mode |
michael@0 | 5940 | if (!DispatchCustomEvent("fullscreen")) { |
michael@0 | 5941 | return NS_OK; |
michael@0 | 5942 | } |
michael@0 | 5943 | |
michael@0 | 5944 | // Prevent chrome documents which are still loading from resizing |
michael@0 | 5945 | // the window after we set fullscreen mode. |
michael@0 | 5946 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 5947 | nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwnerAsWin)); |
michael@0 | 5948 | if (aFullScreen && xulWin) { |
michael@0 | 5949 | xulWin->SetIntrinsicallySized(false); |
michael@0 | 5950 | } |
michael@0 | 5951 | |
michael@0 | 5952 | // Set this before so if widget sends an event indicating its |
michael@0 | 5953 | // gone full screen, the state trap above works. |
michael@0 | 5954 | mFullScreen = aFullScreen; |
michael@0 | 5955 | |
michael@0 | 5956 | // Sometimes we don't want the top-level widget to actually go fullscreen, |
michael@0 | 5957 | // for example in the B2G desktop client, we don't want the emulated screen |
michael@0 | 5958 | // dimensions to appear to increase when entering fullscreen mode; we just |
michael@0 | 5959 | // want the content to fill the entire client area of the emulator window. |
michael@0 | 5960 | if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) { |
michael@0 | 5961 | nsCOMPtr<nsIWidget> widget = GetMainWidget(); |
michael@0 | 5962 | if (widget) |
michael@0 | 5963 | widget->MakeFullScreen(aFullScreen); |
michael@0 | 5964 | } |
michael@0 | 5965 | |
michael@0 | 5966 | if (!mFullScreen) { |
michael@0 | 5967 | // Force exit from DOM full-screen mode. This is so that if we're in |
michael@0 | 5968 | // DOM full-screen mode and the user exits full-screen mode with |
michael@0 | 5969 | // the browser full-screen mode toggle keyboard-shortcut, we'll detect |
michael@0 | 5970 | // that and leave DOM API full-screen mode too. |
michael@0 | 5971 | nsIDocument::ExitFullscreen(mDoc, /* async */ false); |
michael@0 | 5972 | } |
michael@0 | 5973 | |
michael@0 | 5974 | if (!mWakeLock && mFullScreen) { |
michael@0 | 5975 | nsRefPtr<power::PowerManagerService> pmService = |
michael@0 | 5976 | power::PowerManagerService::GetInstance(); |
michael@0 | 5977 | NS_ENSURE_TRUE(pmService, NS_OK); |
michael@0 | 5978 | |
michael@0 | 5979 | ErrorResult rv; |
michael@0 | 5980 | mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("DOM_Fullscreen"), |
michael@0 | 5981 | this, rv); |
michael@0 | 5982 | if (rv.Failed()) { |
michael@0 | 5983 | return rv.ErrorCode(); |
michael@0 | 5984 | } |
michael@0 | 5985 | |
michael@0 | 5986 | } else if (mWakeLock && !mFullScreen) { |
michael@0 | 5987 | ErrorResult rv; |
michael@0 | 5988 | mWakeLock->Unlock(rv); |
michael@0 | 5989 | NS_WARN_IF_FALSE(!rv.Failed(), "Failed to unlock the wakelock."); |
michael@0 | 5990 | mWakeLock = nullptr; |
michael@0 | 5991 | } |
michael@0 | 5992 | |
michael@0 | 5993 | return NS_OK; |
michael@0 | 5994 | } |
michael@0 | 5995 | |
michael@0 | 5996 | bool |
michael@0 | 5997 | nsGlobalWindow::GetFullScreen(ErrorResult& aError) |
michael@0 | 5998 | { |
michael@0 | 5999 | FORWARD_TO_OUTER_OR_THROW(GetFullScreen, (aError), aError, false); |
michael@0 | 6000 | |
michael@0 | 6001 | // Get the fullscreen value of the root window, to always have the value |
michael@0 | 6002 | // accurate, even when called from content. |
michael@0 | 6003 | if (mDocShell) { |
michael@0 | 6004 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
michael@0 | 6005 | mDocShell->GetRootTreeItem(getter_AddRefs(rootItem)); |
michael@0 | 6006 | if (rootItem != mDocShell) { |
michael@0 | 6007 | nsCOMPtr<nsIDOMWindow> window = do_GetInterface(rootItem); |
michael@0 | 6008 | if (window) { |
michael@0 | 6009 | bool fullScreen = false; |
michael@0 | 6010 | aError = window->GetFullScreen(&fullScreen); |
michael@0 | 6011 | return fullScreen; |
michael@0 | 6012 | } |
michael@0 | 6013 | } |
michael@0 | 6014 | } |
michael@0 | 6015 | |
michael@0 | 6016 | // We are the root window, or something went wrong. Return our internal value. |
michael@0 | 6017 | return mFullScreen; |
michael@0 | 6018 | } |
michael@0 | 6019 | |
michael@0 | 6020 | NS_IMETHODIMP |
michael@0 | 6021 | nsGlobalWindow::GetFullScreen(bool* aFullScreen) |
michael@0 | 6022 | { |
michael@0 | 6023 | ErrorResult rv; |
michael@0 | 6024 | *aFullScreen = GetFullScreen(rv); |
michael@0 | 6025 | |
michael@0 | 6026 | return rv.ErrorCode(); |
michael@0 | 6027 | } |
michael@0 | 6028 | |
michael@0 | 6029 | NS_IMETHODIMP |
michael@0 | 6030 | nsGlobalWindow::Dump(const nsAString& aStr) |
michael@0 | 6031 | { |
michael@0 | 6032 | if (!nsContentUtils::DOMWindowDumpEnabled()) { |
michael@0 | 6033 | return NS_OK; |
michael@0 | 6034 | } |
michael@0 | 6035 | |
michael@0 | 6036 | char *cstr = ToNewUTF8String(aStr); |
michael@0 | 6037 | |
michael@0 | 6038 | #if defined(XP_MACOSX) |
michael@0 | 6039 | // have to convert \r to \n so that printing to the console works |
michael@0 | 6040 | char *c = cstr, *cEnd = cstr + strlen(cstr); |
michael@0 | 6041 | while (c < cEnd) { |
michael@0 | 6042 | if (*c == '\r') |
michael@0 | 6043 | *c = '\n'; |
michael@0 | 6044 | c++; |
michael@0 | 6045 | } |
michael@0 | 6046 | #endif |
michael@0 | 6047 | |
michael@0 | 6048 | if (cstr) { |
michael@0 | 6049 | #ifdef XP_WIN |
michael@0 | 6050 | PrintToDebugger(cstr); |
michael@0 | 6051 | #endif |
michael@0 | 6052 | #ifdef ANDROID |
michael@0 | 6053 | __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr); |
michael@0 | 6054 | #endif |
michael@0 | 6055 | FILE *fp = gDumpFile ? gDumpFile : stdout; |
michael@0 | 6056 | fputs(cstr, fp); |
michael@0 | 6057 | fflush(fp); |
michael@0 | 6058 | nsMemory::Free(cstr); |
michael@0 | 6059 | } |
michael@0 | 6060 | |
michael@0 | 6061 | return NS_OK; |
michael@0 | 6062 | } |
michael@0 | 6063 | |
michael@0 | 6064 | void |
michael@0 | 6065 | nsGlobalWindow::EnsureReflowFlushAndPaint() |
michael@0 | 6066 | { |
michael@0 | 6067 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 6068 | NS_ASSERTION(mDocShell, "EnsureReflowFlushAndPaint() called with no " |
michael@0 | 6069 | "docshell!"); |
michael@0 | 6070 | |
michael@0 | 6071 | if (!mDocShell) |
michael@0 | 6072 | return; |
michael@0 | 6073 | |
michael@0 | 6074 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 6075 | |
michael@0 | 6076 | if (!presShell) |
michael@0 | 6077 | return; |
michael@0 | 6078 | |
michael@0 | 6079 | // Flush pending reflows. |
michael@0 | 6080 | if (mDoc) { |
michael@0 | 6081 | mDoc->FlushPendingNotifications(Flush_Layout); |
michael@0 | 6082 | } |
michael@0 | 6083 | |
michael@0 | 6084 | // Unsuppress painting. |
michael@0 | 6085 | presShell->UnsuppressPainting(); |
michael@0 | 6086 | } |
michael@0 | 6087 | |
michael@0 | 6088 | NS_IMETHODIMP |
michael@0 | 6089 | nsGlobalWindow::GetTextZoom(float *aZoom) |
michael@0 | 6090 | { |
michael@0 | 6091 | FORWARD_TO_OUTER(GetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 6092 | |
michael@0 | 6093 | if (mDocShell) { |
michael@0 | 6094 | nsCOMPtr<nsIContentViewer> contentViewer; |
michael@0 | 6095 | mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); |
michael@0 | 6096 | nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(contentViewer)); |
michael@0 | 6097 | |
michael@0 | 6098 | if (markupViewer) { |
michael@0 | 6099 | return markupViewer->GetTextZoom(aZoom); |
michael@0 | 6100 | } |
michael@0 | 6101 | } |
michael@0 | 6102 | return NS_ERROR_FAILURE; |
michael@0 | 6103 | } |
michael@0 | 6104 | |
michael@0 | 6105 | NS_IMETHODIMP |
michael@0 | 6106 | nsGlobalWindow::SetTextZoom(float aZoom) |
michael@0 | 6107 | { |
michael@0 | 6108 | FORWARD_TO_OUTER(SetTextZoom, (aZoom), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 6109 | |
michael@0 | 6110 | if (mDocShell) { |
michael@0 | 6111 | nsCOMPtr<nsIContentViewer> contentViewer; |
michael@0 | 6112 | mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); |
michael@0 | 6113 | nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(contentViewer)); |
michael@0 | 6114 | |
michael@0 | 6115 | if (markupViewer) |
michael@0 | 6116 | return markupViewer->SetTextZoom(aZoom); |
michael@0 | 6117 | } |
michael@0 | 6118 | return NS_ERROR_FAILURE; |
michael@0 | 6119 | } |
michael@0 | 6120 | |
michael@0 | 6121 | // static |
michael@0 | 6122 | void |
michael@0 | 6123 | nsGlobalWindow::MakeScriptDialogTitle(nsAString &aOutTitle) |
michael@0 | 6124 | { |
michael@0 | 6125 | aOutTitle.Truncate(); |
michael@0 | 6126 | |
michael@0 | 6127 | // Try to get a host from the running principal -- this will do the |
michael@0 | 6128 | // right thing for javascript: and data: documents. |
michael@0 | 6129 | |
michael@0 | 6130 | nsresult rv = NS_OK; |
michael@0 | 6131 | NS_ASSERTION(nsContentUtils::GetSecurityManager(), |
michael@0 | 6132 | "Global Window has no security manager!"); |
michael@0 | 6133 | if (nsContentUtils::GetSecurityManager()) { |
michael@0 | 6134 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 6135 | rv = nsContentUtils::GetSecurityManager()-> |
michael@0 | 6136 | GetSubjectPrincipal(getter_AddRefs(principal)); |
michael@0 | 6137 | |
michael@0 | 6138 | if (NS_SUCCEEDED(rv) && principal) { |
michael@0 | 6139 | nsCOMPtr<nsIURI> uri; |
michael@0 | 6140 | rv = principal->GetURI(getter_AddRefs(uri)); |
michael@0 | 6141 | |
michael@0 | 6142 | if (NS_SUCCEEDED(rv) && uri) { |
michael@0 | 6143 | // remove user:pass for privacy and spoof prevention |
michael@0 | 6144 | |
michael@0 | 6145 | nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID)); |
michael@0 | 6146 | if (fixup) { |
michael@0 | 6147 | nsCOMPtr<nsIURI> fixedURI; |
michael@0 | 6148 | rv = fixup->CreateExposableURI(uri, getter_AddRefs(fixedURI)); |
michael@0 | 6149 | if (NS_SUCCEEDED(rv) && fixedURI) { |
michael@0 | 6150 | nsAutoCString host; |
michael@0 | 6151 | fixedURI->GetHost(host); |
michael@0 | 6152 | |
michael@0 | 6153 | if (!host.IsEmpty()) { |
michael@0 | 6154 | // if this URI has a host we'll show it. For other |
michael@0 | 6155 | // schemes (e.g. file:) we fall back to the localized |
michael@0 | 6156 | // generic string |
michael@0 | 6157 | |
michael@0 | 6158 | nsAutoCString prepath; |
michael@0 | 6159 | fixedURI->GetPrePath(prepath); |
michael@0 | 6160 | |
michael@0 | 6161 | NS_ConvertUTF8toUTF16 ucsPrePath(prepath); |
michael@0 | 6162 | const char16_t *formatStrings[] = { ucsPrePath.get() }; |
michael@0 | 6163 | nsXPIDLString tempString; |
michael@0 | 6164 | nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, |
michael@0 | 6165 | "ScriptDlgHeading", |
michael@0 | 6166 | formatStrings, |
michael@0 | 6167 | tempString); |
michael@0 | 6168 | aOutTitle = tempString; |
michael@0 | 6169 | } |
michael@0 | 6170 | } |
michael@0 | 6171 | } |
michael@0 | 6172 | } |
michael@0 | 6173 | } |
michael@0 | 6174 | else { // failed to get subject principal |
michael@0 | 6175 | NS_WARNING("No script principal? Who is calling alert/confirm/prompt?!"); |
michael@0 | 6176 | } |
michael@0 | 6177 | } |
michael@0 | 6178 | |
michael@0 | 6179 | if (aOutTitle.IsEmpty()) { |
michael@0 | 6180 | // We didn't find a host so use the generic heading |
michael@0 | 6181 | nsXPIDLString tempString; |
michael@0 | 6182 | nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, |
michael@0 | 6183 | "ScriptDlgGenericHeading", |
michael@0 | 6184 | tempString); |
michael@0 | 6185 | aOutTitle = tempString; |
michael@0 | 6186 | } |
michael@0 | 6187 | |
michael@0 | 6188 | // Just in case |
michael@0 | 6189 | if (aOutTitle.IsEmpty()) { |
michael@0 | 6190 | NS_WARNING("could not get ScriptDlgGenericHeading string from string bundle"); |
michael@0 | 6191 | aOutTitle.AssignLiteral("[Script]"); |
michael@0 | 6192 | } |
michael@0 | 6193 | } |
michael@0 | 6194 | |
michael@0 | 6195 | bool |
michael@0 | 6196 | nsGlobalWindow::CanMoveResizeWindows() |
michael@0 | 6197 | { |
michael@0 | 6198 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 6199 | |
michael@0 | 6200 | // When called from chrome, we can avoid the following checks. |
michael@0 | 6201 | if (!nsContentUtils::IsCallerChrome()) { |
michael@0 | 6202 | // Don't allow scripts to move or resize windows that were not opened by a |
michael@0 | 6203 | // script. |
michael@0 | 6204 | if (!mHadOriginalOpener) { |
michael@0 | 6205 | return false; |
michael@0 | 6206 | } |
michael@0 | 6207 | |
michael@0 | 6208 | if (!CanSetProperty("dom.disable_window_move_resize")) { |
michael@0 | 6209 | return false; |
michael@0 | 6210 | } |
michael@0 | 6211 | |
michael@0 | 6212 | // Ignore the request if we have more than one tab in the window. |
michael@0 | 6213 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); |
michael@0 | 6214 | if (treeOwner) { |
michael@0 | 6215 | uint32_t itemCount; |
michael@0 | 6216 | if (NS_SUCCEEDED(treeOwner->GetTargetableShellCount(&itemCount)) && |
michael@0 | 6217 | itemCount > 1) { |
michael@0 | 6218 | return false; |
michael@0 | 6219 | } |
michael@0 | 6220 | } |
michael@0 | 6221 | } |
michael@0 | 6222 | |
michael@0 | 6223 | // The preference is useful for the webapp runtime. Webapps should be able |
michael@0 | 6224 | // to resize or move their window. |
michael@0 | 6225 | if (mDocShell && !Preferences::GetBool("dom.always_allow_move_resize_window", |
michael@0 | 6226 | false)) { |
michael@0 | 6227 | bool allow; |
michael@0 | 6228 | nsresult rv = mDocShell->GetAllowWindowControl(&allow); |
michael@0 | 6229 | if (NS_SUCCEEDED(rv) && !allow) |
michael@0 | 6230 | return false; |
michael@0 | 6231 | } |
michael@0 | 6232 | |
michael@0 | 6233 | if (gMouseDown && !gDragServiceDisabled) { |
michael@0 | 6234 | nsCOMPtr<nsIDragService> ds = |
michael@0 | 6235 | do_GetService("@mozilla.org/widget/dragservice;1"); |
michael@0 | 6236 | if (ds) { |
michael@0 | 6237 | gDragServiceDisabled = true; |
michael@0 | 6238 | ds->Suppress(); |
michael@0 | 6239 | } |
michael@0 | 6240 | } |
michael@0 | 6241 | return true; |
michael@0 | 6242 | } |
michael@0 | 6243 | |
michael@0 | 6244 | bool |
michael@0 | 6245 | nsGlobalWindow::AlertOrConfirm(bool aAlert, |
michael@0 | 6246 | const nsAString& aMessage, |
michael@0 | 6247 | mozilla::ErrorResult& aError) |
michael@0 | 6248 | { |
michael@0 | 6249 | // XXX This method is very similar to nsGlobalWindow::Prompt, make |
michael@0 | 6250 | // sure any modifications here don't need to happen over there! |
michael@0 | 6251 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 6252 | |
michael@0 | 6253 | if (!AreDialogsEnabled()) { |
michael@0 | 6254 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 6255 | return false; |
michael@0 | 6256 | } |
michael@0 | 6257 | |
michael@0 | 6258 | // Reset popup state while opening a modal dialog, and firing events |
michael@0 | 6259 | // about the dialog, to prevent the current state from being active |
michael@0 | 6260 | // the whole time a modal dialog is open. |
michael@0 | 6261 | nsAutoPopupStatePusher popupStatePusher(openAbused, true); |
michael@0 | 6262 | |
michael@0 | 6263 | // Before bringing up the window, unsuppress painting and flush |
michael@0 | 6264 | // pending reflows. |
michael@0 | 6265 | EnsureReflowFlushAndPaint(); |
michael@0 | 6266 | |
michael@0 | 6267 | nsAutoString title; |
michael@0 | 6268 | MakeScriptDialogTitle(title); |
michael@0 | 6269 | |
michael@0 | 6270 | // Remove non-terminating null characters from the |
michael@0 | 6271 | // string. See bug #310037. |
michael@0 | 6272 | nsAutoString final; |
michael@0 | 6273 | nsContentUtils::StripNullChars(aMessage, final); |
michael@0 | 6274 | |
michael@0 | 6275 | nsresult rv; |
michael@0 | 6276 | nsCOMPtr<nsIPromptFactory> promptFac = |
michael@0 | 6277 | do_GetService("@mozilla.org/prompter;1", &rv); |
michael@0 | 6278 | if (NS_FAILED(rv)) { |
michael@0 | 6279 | aError.Throw(rv); |
michael@0 | 6280 | return false; |
michael@0 | 6281 | } |
michael@0 | 6282 | |
michael@0 | 6283 | nsCOMPtr<nsIPrompt> prompt; |
michael@0 | 6284 | aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), |
michael@0 | 6285 | getter_AddRefs(prompt)); |
michael@0 | 6286 | if (aError.Failed()) { |
michael@0 | 6287 | return false; |
michael@0 | 6288 | } |
michael@0 | 6289 | |
michael@0 | 6290 | // Always allow tab modal prompts for alert and confirm. |
michael@0 | 6291 | if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) { |
michael@0 | 6292 | promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true); |
michael@0 | 6293 | } |
michael@0 | 6294 | |
michael@0 | 6295 | bool result = false; |
michael@0 | 6296 | nsAutoSyncOperation sync(mDoc); |
michael@0 | 6297 | if (ShouldPromptToBlockDialogs()) { |
michael@0 | 6298 | bool disallowDialog = false; |
michael@0 | 6299 | nsXPIDLString label; |
michael@0 | 6300 | nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, |
michael@0 | 6301 | "ScriptDialogLabel", label); |
michael@0 | 6302 | |
michael@0 | 6303 | aError = aAlert ? |
michael@0 | 6304 | prompt->AlertCheck(title.get(), final.get(), label.get(), |
michael@0 | 6305 | &disallowDialog) : |
michael@0 | 6306 | prompt->ConfirmCheck(title.get(), final.get(), label.get(), |
michael@0 | 6307 | &disallowDialog, &result); |
michael@0 | 6308 | |
michael@0 | 6309 | if (disallowDialog) |
michael@0 | 6310 | DisableDialogs(); |
michael@0 | 6311 | } else { |
michael@0 | 6312 | aError = aAlert ? |
michael@0 | 6313 | prompt->Alert(title.get(), final.get()) : |
michael@0 | 6314 | prompt->Confirm(title.get(), final.get(), &result); |
michael@0 | 6315 | } |
michael@0 | 6316 | |
michael@0 | 6317 | return result; |
michael@0 | 6318 | } |
michael@0 | 6319 | |
michael@0 | 6320 | void |
michael@0 | 6321 | nsGlobalWindow::Alert(const nsAString& aMessage, mozilla::ErrorResult& aError) |
michael@0 | 6322 | { |
michael@0 | 6323 | FORWARD_TO_OUTER_OR_THROW(Alert, (aMessage, aError), aError, ); |
michael@0 | 6324 | AlertOrConfirm(/* aAlert = */ true, aMessage, aError); |
michael@0 | 6325 | } |
michael@0 | 6326 | |
michael@0 | 6327 | NS_IMETHODIMP |
michael@0 | 6328 | nsGlobalWindow::Alert(const nsAString& aString) |
michael@0 | 6329 | { |
michael@0 | 6330 | ErrorResult rv; |
michael@0 | 6331 | Alert(aString, rv); |
michael@0 | 6332 | |
michael@0 | 6333 | return rv.ErrorCode(); |
michael@0 | 6334 | } |
michael@0 | 6335 | |
michael@0 | 6336 | bool |
michael@0 | 6337 | nsGlobalWindow::Confirm(const nsAString& aMessage, ErrorResult& aError) |
michael@0 | 6338 | { |
michael@0 | 6339 | FORWARD_TO_OUTER_OR_THROW(Confirm, (aMessage, aError), aError, false); |
michael@0 | 6340 | |
michael@0 | 6341 | return AlertOrConfirm(/* aAlert = */ false, aMessage, aError); |
michael@0 | 6342 | } |
michael@0 | 6343 | |
michael@0 | 6344 | NS_IMETHODIMP |
michael@0 | 6345 | nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn) |
michael@0 | 6346 | { |
michael@0 | 6347 | ErrorResult rv; |
michael@0 | 6348 | *aReturn = Confirm(aString, rv); |
michael@0 | 6349 | |
michael@0 | 6350 | return rv.ErrorCode(); |
michael@0 | 6351 | } |
michael@0 | 6352 | |
michael@0 | 6353 | void |
michael@0 | 6354 | nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial, |
michael@0 | 6355 | nsAString& aReturn, ErrorResult& aError) |
michael@0 | 6356 | { |
michael@0 | 6357 | // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make |
michael@0 | 6358 | // sure any modifications here don't need to happen over there! |
michael@0 | 6359 | FORWARD_TO_OUTER_OR_THROW(Prompt, (aMessage, aInitial, aReturn, aError), |
michael@0 | 6360 | aError, ); |
michael@0 | 6361 | |
michael@0 | 6362 | SetDOMStringToNull(aReturn); |
michael@0 | 6363 | |
michael@0 | 6364 | if (!AreDialogsEnabled()) { |
michael@0 | 6365 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 6366 | return; |
michael@0 | 6367 | } |
michael@0 | 6368 | |
michael@0 | 6369 | // Reset popup state while opening a modal dialog, and firing events |
michael@0 | 6370 | // about the dialog, to prevent the current state from being active |
michael@0 | 6371 | // the whole time a modal dialog is open. |
michael@0 | 6372 | nsAutoPopupStatePusher popupStatePusher(openAbused, true); |
michael@0 | 6373 | |
michael@0 | 6374 | // Before bringing up the window, unsuppress painting and flush |
michael@0 | 6375 | // pending reflows. |
michael@0 | 6376 | EnsureReflowFlushAndPaint(); |
michael@0 | 6377 | |
michael@0 | 6378 | nsAutoString title; |
michael@0 | 6379 | MakeScriptDialogTitle(title); |
michael@0 | 6380 | |
michael@0 | 6381 | // Remove non-terminating null characters from the |
michael@0 | 6382 | // string. See bug #310037. |
michael@0 | 6383 | nsAutoString fixedMessage, fixedInitial; |
michael@0 | 6384 | nsContentUtils::StripNullChars(aMessage, fixedMessage); |
michael@0 | 6385 | nsContentUtils::StripNullChars(aInitial, fixedInitial); |
michael@0 | 6386 | |
michael@0 | 6387 | nsresult rv; |
michael@0 | 6388 | nsCOMPtr<nsIPromptFactory> promptFac = |
michael@0 | 6389 | do_GetService("@mozilla.org/prompter;1", &rv); |
michael@0 | 6390 | if (NS_FAILED(rv)) { |
michael@0 | 6391 | aError.Throw(rv); |
michael@0 | 6392 | return; |
michael@0 | 6393 | } |
michael@0 | 6394 | |
michael@0 | 6395 | nsCOMPtr<nsIPrompt> prompt; |
michael@0 | 6396 | aError = promptFac->GetPrompt(this, NS_GET_IID(nsIPrompt), |
michael@0 | 6397 | getter_AddRefs(prompt)); |
michael@0 | 6398 | if (aError.Failed()) { |
michael@0 | 6399 | return; |
michael@0 | 6400 | } |
michael@0 | 6401 | |
michael@0 | 6402 | // Always allow tab modal prompts for prompt. |
michael@0 | 6403 | if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompt)) { |
michael@0 | 6404 | promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true); |
michael@0 | 6405 | } |
michael@0 | 6406 | |
michael@0 | 6407 | // Pass in the default value, if any. |
michael@0 | 6408 | char16_t *inoutValue = ToNewUnicode(fixedInitial); |
michael@0 | 6409 | bool disallowDialog = false; |
michael@0 | 6410 | |
michael@0 | 6411 | nsXPIDLString label; |
michael@0 | 6412 | if (ShouldPromptToBlockDialogs()) { |
michael@0 | 6413 | nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, |
michael@0 | 6414 | "ScriptDialogLabel", label); |
michael@0 | 6415 | } |
michael@0 | 6416 | |
michael@0 | 6417 | nsAutoSyncOperation sync(mDoc); |
michael@0 | 6418 | bool ok; |
michael@0 | 6419 | aError = prompt->Prompt(title.get(), fixedMessage.get(), |
michael@0 | 6420 | &inoutValue, label.get(), &disallowDialog, &ok); |
michael@0 | 6421 | |
michael@0 | 6422 | if (disallowDialog) { |
michael@0 | 6423 | DisableDialogs(); |
michael@0 | 6424 | } |
michael@0 | 6425 | |
michael@0 | 6426 | if (aError.Failed()) { |
michael@0 | 6427 | return; |
michael@0 | 6428 | } |
michael@0 | 6429 | |
michael@0 | 6430 | nsAdoptingString outValue(inoutValue); |
michael@0 | 6431 | |
michael@0 | 6432 | if (ok && outValue) { |
michael@0 | 6433 | aReturn.Assign(outValue); |
michael@0 | 6434 | } |
michael@0 | 6435 | } |
michael@0 | 6436 | |
michael@0 | 6437 | NS_IMETHODIMP |
michael@0 | 6438 | nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial, |
michael@0 | 6439 | nsAString& aReturn) |
michael@0 | 6440 | { |
michael@0 | 6441 | ErrorResult rv; |
michael@0 | 6442 | Prompt(aMessage, aInitial, aReturn, rv); |
michael@0 | 6443 | |
michael@0 | 6444 | return rv.ErrorCode(); |
michael@0 | 6445 | } |
michael@0 | 6446 | |
michael@0 | 6447 | void |
michael@0 | 6448 | nsGlobalWindow::Focus(ErrorResult& aError) |
michael@0 | 6449 | { |
michael@0 | 6450 | FORWARD_TO_OUTER_OR_THROW(Focus, (aError), aError, ); |
michael@0 | 6451 | |
michael@0 | 6452 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 6453 | if (!fm) { |
michael@0 | 6454 | return; |
michael@0 | 6455 | } |
michael@0 | 6456 | |
michael@0 | 6457 | nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell); |
michael@0 | 6458 | |
michael@0 | 6459 | bool isVisible = false; |
michael@0 | 6460 | if (baseWin) { |
michael@0 | 6461 | baseWin->GetVisibility(&isVisible); |
michael@0 | 6462 | } |
michael@0 | 6463 | |
michael@0 | 6464 | if (!isVisible) { |
michael@0 | 6465 | // A hidden tab is being focused, ignore this call. |
michael@0 | 6466 | return; |
michael@0 | 6467 | } |
michael@0 | 6468 | |
michael@0 | 6469 | nsIDOMWindow *caller = nsContentUtils::GetWindowFromCaller(); |
michael@0 | 6470 | nsCOMPtr<nsIDOMWindow> opener; |
michael@0 | 6471 | GetOpener(getter_AddRefs(opener)); |
michael@0 | 6472 | |
michael@0 | 6473 | // Enforce dom.disable_window_flip (for non-chrome), but still allow the |
michael@0 | 6474 | // window which opened us to raise us at times when popups are allowed |
michael@0 | 6475 | // (bugs 355482 and 369306). |
michael@0 | 6476 | bool canFocus = CanSetProperty("dom.disable_window_flip") || |
michael@0 | 6477 | (opener == caller && |
michael@0 | 6478 | RevisePopupAbuseLevel(gPopupControlState) < openAbused); |
michael@0 | 6479 | |
michael@0 | 6480 | nsCOMPtr<nsIDOMWindow> activeWindow; |
michael@0 | 6481 | fm->GetActiveWindow(getter_AddRefs(activeWindow)); |
michael@0 | 6482 | |
michael@0 | 6483 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
michael@0 | 6484 | mDocShell->GetRootTreeItem(getter_AddRefs(rootItem)); |
michael@0 | 6485 | nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem); |
michael@0 | 6486 | bool isActive = (rootWin == activeWindow); |
michael@0 | 6487 | |
michael@0 | 6488 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 6489 | if (treeOwnerAsWin && (canFocus || isActive)) { |
michael@0 | 6490 | bool isEnabled = true; |
michael@0 | 6491 | if (NS_SUCCEEDED(treeOwnerAsWin->GetEnabled(&isEnabled)) && !isEnabled) { |
michael@0 | 6492 | NS_WARNING( "Should not try to set the focus on a disabled window" ); |
michael@0 | 6493 | return; |
michael@0 | 6494 | } |
michael@0 | 6495 | |
michael@0 | 6496 | // XXXndeakin not sure what this is for or if it should go somewhere else |
michael@0 | 6497 | nsCOMPtr<nsIEmbeddingSiteWindow> embeddingWin(do_GetInterface(treeOwnerAsWin)); |
michael@0 | 6498 | if (embeddingWin) |
michael@0 | 6499 | embeddingWin->SetFocus(); |
michael@0 | 6500 | } |
michael@0 | 6501 | |
michael@0 | 6502 | if (!mDocShell) { |
michael@0 | 6503 | return; |
michael@0 | 6504 | } |
michael@0 | 6505 | |
michael@0 | 6506 | nsCOMPtr<nsIPresShell> presShell; |
michael@0 | 6507 | // Don't look for a presshell if we're a root chrome window that's got |
michael@0 | 6508 | // about:blank loaded. We don't want to focus our widget in that case. |
michael@0 | 6509 | // XXXbz should we really be checking for IsInitialDocument() instead? |
michael@0 | 6510 | bool lookForPresShell = true; |
michael@0 | 6511 | if (mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome && |
michael@0 | 6512 | GetPrivateRoot() == static_cast<nsIDOMWindow*>(this) && |
michael@0 | 6513 | mDoc) { |
michael@0 | 6514 | nsIURI* ourURI = mDoc->GetDocumentURI(); |
michael@0 | 6515 | if (ourURI) { |
michael@0 | 6516 | lookForPresShell = !NS_IsAboutBlank(ourURI); |
michael@0 | 6517 | } |
michael@0 | 6518 | } |
michael@0 | 6519 | |
michael@0 | 6520 | if (lookForPresShell) { |
michael@0 | 6521 | mDocShell->GetEldestPresShell(getter_AddRefs(presShell)); |
michael@0 | 6522 | } |
michael@0 | 6523 | |
michael@0 | 6524 | nsCOMPtr<nsIDocShellTreeItem> parentDsti; |
michael@0 | 6525 | mDocShell->GetParent(getter_AddRefs(parentDsti)); |
michael@0 | 6526 | |
michael@0 | 6527 | // set the parent's current focus to the frame containing this window. |
michael@0 | 6528 | nsCOMPtr<nsPIDOMWindow> parent = do_GetInterface(parentDsti); |
michael@0 | 6529 | if (parent) { |
michael@0 | 6530 | nsCOMPtr<nsIDocument> parentdoc = parent->GetDoc(); |
michael@0 | 6531 | if (!parentdoc) { |
michael@0 | 6532 | return; |
michael@0 | 6533 | } |
michael@0 | 6534 | |
michael@0 | 6535 | nsIContent* frame = parentdoc->FindContentForSubDocument(mDoc); |
michael@0 | 6536 | nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(frame); |
michael@0 | 6537 | if (frameElement) { |
michael@0 | 6538 | uint32_t flags = nsIFocusManager::FLAG_NOSCROLL; |
michael@0 | 6539 | if (canFocus) |
michael@0 | 6540 | flags |= nsIFocusManager::FLAG_RAISE; |
michael@0 | 6541 | aError = fm->SetFocus(frameElement, flags); |
michael@0 | 6542 | } |
michael@0 | 6543 | return; |
michael@0 | 6544 | } |
michael@0 | 6545 | if (nsCOMPtr<nsITabChild> child = do_GetInterface(mDocShell)) { |
michael@0 | 6546 | child->SendRequestFocus(canFocus); |
michael@0 | 6547 | return; |
michael@0 | 6548 | } |
michael@0 | 6549 | if (canFocus) { |
michael@0 | 6550 | // if there is no parent, this must be a toplevel window, so raise the |
michael@0 | 6551 | // window if canFocus is true |
michael@0 | 6552 | aError = fm->SetActiveWindow(this); |
michael@0 | 6553 | } |
michael@0 | 6554 | } |
michael@0 | 6555 | |
michael@0 | 6556 | NS_IMETHODIMP |
michael@0 | 6557 | nsGlobalWindow::Focus() |
michael@0 | 6558 | { |
michael@0 | 6559 | ErrorResult rv; |
michael@0 | 6560 | Focus(rv); |
michael@0 | 6561 | |
michael@0 | 6562 | return rv.ErrorCode(); |
michael@0 | 6563 | } |
michael@0 | 6564 | |
michael@0 | 6565 | void |
michael@0 | 6566 | nsGlobalWindow::Blur(ErrorResult& aError) |
michael@0 | 6567 | { |
michael@0 | 6568 | FORWARD_TO_OUTER_OR_THROW(Blur, (aError), aError, ); |
michael@0 | 6569 | |
michael@0 | 6570 | // If dom.disable_window_flip == true, then content should not be allowed |
michael@0 | 6571 | // to call this function (this would allow popunders, bug 369306) |
michael@0 | 6572 | if (!CanSetProperty("dom.disable_window_flip")) { |
michael@0 | 6573 | return; |
michael@0 | 6574 | } |
michael@0 | 6575 | |
michael@0 | 6576 | // If embedding apps don't implement nsIEmbeddingSiteWindow, we |
michael@0 | 6577 | // shouldn't throw exceptions to web content. |
michael@0 | 6578 | |
michael@0 | 6579 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); |
michael@0 | 6580 | nsCOMPtr<nsIEmbeddingSiteWindow> siteWindow(do_GetInterface(treeOwner)); |
michael@0 | 6581 | if (siteWindow) { |
michael@0 | 6582 | // This method call may cause mDocShell to become nullptr. |
michael@0 | 6583 | siteWindow->Blur(); |
michael@0 | 6584 | |
michael@0 | 6585 | // if the root is focused, clear the focus |
michael@0 | 6586 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 6587 | if (fm && mDoc) { |
michael@0 | 6588 | nsCOMPtr<nsIDOMElement> element; |
michael@0 | 6589 | fm->GetFocusedElementForWindow(this, false, nullptr, getter_AddRefs(element)); |
michael@0 | 6590 | nsCOMPtr<nsIContent> content = do_QueryInterface(element); |
michael@0 | 6591 | if (content == mDoc->GetRootElement()) |
michael@0 | 6592 | fm->ClearFocus(this); |
michael@0 | 6593 | } |
michael@0 | 6594 | } |
michael@0 | 6595 | } |
michael@0 | 6596 | |
michael@0 | 6597 | NS_IMETHODIMP |
michael@0 | 6598 | nsGlobalWindow::Blur() |
michael@0 | 6599 | { |
michael@0 | 6600 | ErrorResult rv; |
michael@0 | 6601 | Blur(rv); |
michael@0 | 6602 | |
michael@0 | 6603 | return rv.ErrorCode(); |
michael@0 | 6604 | } |
michael@0 | 6605 | |
michael@0 | 6606 | void |
michael@0 | 6607 | nsGlobalWindow::Back(ErrorResult& aError) |
michael@0 | 6608 | { |
michael@0 | 6609 | FORWARD_TO_OUTER_OR_THROW(Back, (aError), aError, ); |
michael@0 | 6610 | |
michael@0 | 6611 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); |
michael@0 | 6612 | if (!webNav) { |
michael@0 | 6613 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6614 | return; |
michael@0 | 6615 | } |
michael@0 | 6616 | |
michael@0 | 6617 | aError = webNav->GoBack(); |
michael@0 | 6618 | } |
michael@0 | 6619 | |
michael@0 | 6620 | NS_IMETHODIMP |
michael@0 | 6621 | nsGlobalWindow::Back() |
michael@0 | 6622 | { |
michael@0 | 6623 | ErrorResult rv; |
michael@0 | 6624 | Back(rv); |
michael@0 | 6625 | |
michael@0 | 6626 | return rv.ErrorCode(); |
michael@0 | 6627 | } |
michael@0 | 6628 | |
michael@0 | 6629 | void |
michael@0 | 6630 | nsGlobalWindow::Forward(ErrorResult& aError) |
michael@0 | 6631 | { |
michael@0 | 6632 | FORWARD_TO_OUTER_OR_THROW(Forward, (aError), aError, ); |
michael@0 | 6633 | |
michael@0 | 6634 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); |
michael@0 | 6635 | if (!webNav) { |
michael@0 | 6636 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6637 | return; |
michael@0 | 6638 | } |
michael@0 | 6639 | |
michael@0 | 6640 | aError = webNav->GoForward(); |
michael@0 | 6641 | } |
michael@0 | 6642 | |
michael@0 | 6643 | NS_IMETHODIMP |
michael@0 | 6644 | nsGlobalWindow::Forward() |
michael@0 | 6645 | { |
michael@0 | 6646 | ErrorResult rv; |
michael@0 | 6647 | Forward(rv); |
michael@0 | 6648 | |
michael@0 | 6649 | return rv.ErrorCode(); |
michael@0 | 6650 | } |
michael@0 | 6651 | |
michael@0 | 6652 | void |
michael@0 | 6653 | nsGlobalWindow::Home(ErrorResult& aError) |
michael@0 | 6654 | { |
michael@0 | 6655 | FORWARD_TO_OUTER_OR_THROW(Home, (aError), aError, ); |
michael@0 | 6656 | |
michael@0 | 6657 | if (!mDocShell) { |
michael@0 | 6658 | return; |
michael@0 | 6659 | } |
michael@0 | 6660 | |
michael@0 | 6661 | nsAdoptingString homeURL = |
michael@0 | 6662 | Preferences::GetLocalizedString(PREF_BROWSER_STARTUP_HOMEPAGE); |
michael@0 | 6663 | |
michael@0 | 6664 | if (homeURL.IsEmpty()) { |
michael@0 | 6665 | // if all else fails, use this |
michael@0 | 6666 | #ifdef DEBUG_seth |
michael@0 | 6667 | printf("all else failed. using %s as the home page\n", DEFAULT_HOME_PAGE); |
michael@0 | 6668 | #endif |
michael@0 | 6669 | CopyASCIItoUTF16(DEFAULT_HOME_PAGE, homeURL); |
michael@0 | 6670 | } |
michael@0 | 6671 | |
michael@0 | 6672 | #ifdef MOZ_PHOENIX |
michael@0 | 6673 | { |
michael@0 | 6674 | // Firefox lets the user specify multiple home pages to open in |
michael@0 | 6675 | // individual tabs by separating them with '|'. Since we don't |
michael@0 | 6676 | // have the machinery in place to easily open new tabs from here, |
michael@0 | 6677 | // simply truncate the homeURL at the first '|' character to |
michael@0 | 6678 | // prevent any possibilities of leaking the users list of home |
michael@0 | 6679 | // pages to the first home page. |
michael@0 | 6680 | // |
michael@0 | 6681 | // Once bug https://bugzilla.mozilla.org/show_bug.cgi?id=221445 is |
michael@0 | 6682 | // fixed we can revisit this. |
michael@0 | 6683 | int32_t firstPipe = homeURL.FindChar('|'); |
michael@0 | 6684 | |
michael@0 | 6685 | if (firstPipe > 0) { |
michael@0 | 6686 | homeURL.Truncate(firstPipe); |
michael@0 | 6687 | } |
michael@0 | 6688 | } |
michael@0 | 6689 | #endif |
michael@0 | 6690 | |
michael@0 | 6691 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); |
michael@0 | 6692 | if (!webNav) { |
michael@0 | 6693 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6694 | return; |
michael@0 | 6695 | } |
michael@0 | 6696 | |
michael@0 | 6697 | aError = webNav->LoadURI(homeURL.get(), |
michael@0 | 6698 | nsIWebNavigation::LOAD_FLAGS_NONE, |
michael@0 | 6699 | nullptr, |
michael@0 | 6700 | nullptr, |
michael@0 | 6701 | nullptr); |
michael@0 | 6702 | } |
michael@0 | 6703 | |
michael@0 | 6704 | NS_IMETHODIMP |
michael@0 | 6705 | nsGlobalWindow::Home() |
michael@0 | 6706 | { |
michael@0 | 6707 | ErrorResult rv; |
michael@0 | 6708 | Home(rv); |
michael@0 | 6709 | |
michael@0 | 6710 | return rv.ErrorCode(); |
michael@0 | 6711 | } |
michael@0 | 6712 | |
michael@0 | 6713 | void |
michael@0 | 6714 | nsGlobalWindow::Stop(ErrorResult& aError) |
michael@0 | 6715 | { |
michael@0 | 6716 | FORWARD_TO_OUTER_OR_THROW(Stop, (aError), aError, ); |
michael@0 | 6717 | |
michael@0 | 6718 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); |
michael@0 | 6719 | if (webNav) { |
michael@0 | 6720 | aError = webNav->Stop(nsIWebNavigation::STOP_ALL); |
michael@0 | 6721 | } |
michael@0 | 6722 | } |
michael@0 | 6723 | |
michael@0 | 6724 | NS_IMETHODIMP |
michael@0 | 6725 | nsGlobalWindow::Stop() |
michael@0 | 6726 | { |
michael@0 | 6727 | ErrorResult rv; |
michael@0 | 6728 | Stop(rv); |
michael@0 | 6729 | |
michael@0 | 6730 | return rv.ErrorCode(); |
michael@0 | 6731 | } |
michael@0 | 6732 | |
michael@0 | 6733 | void |
michael@0 | 6734 | nsGlobalWindow::Print(ErrorResult& aError) |
michael@0 | 6735 | { |
michael@0 | 6736 | #ifdef NS_PRINTING |
michael@0 | 6737 | FORWARD_TO_OUTER_OR_THROW(Print, (aError), aError, ); |
michael@0 | 6738 | |
michael@0 | 6739 | if (Preferences::GetBool("dom.disable_window_print", false)) { |
michael@0 | 6740 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 6741 | return; |
michael@0 | 6742 | } |
michael@0 | 6743 | |
michael@0 | 6744 | if (!AreDialogsEnabled()) { |
michael@0 | 6745 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 6746 | return; |
michael@0 | 6747 | } |
michael@0 | 6748 | |
michael@0 | 6749 | if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) { |
michael@0 | 6750 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 6751 | return; |
michael@0 | 6752 | } |
michael@0 | 6753 | |
michael@0 | 6754 | nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint; |
michael@0 | 6755 | if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint), |
michael@0 | 6756 | getter_AddRefs(webBrowserPrint)))) { |
michael@0 | 6757 | nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ? |
michael@0 | 6758 | GetCurrentInnerWindowInternal()->mDoc : |
michael@0 | 6759 | nullptr); |
michael@0 | 6760 | |
michael@0 | 6761 | nsCOMPtr<nsIPrintSettingsService> printSettingsService = |
michael@0 | 6762 | do_GetService("@mozilla.org/gfx/printsettings-service;1"); |
michael@0 | 6763 | |
michael@0 | 6764 | nsCOMPtr<nsIPrintSettings> printSettings; |
michael@0 | 6765 | if (printSettingsService) { |
michael@0 | 6766 | bool printSettingsAreGlobal = |
michael@0 | 6767 | Preferences::GetBool("print.use_global_printsettings", false); |
michael@0 | 6768 | |
michael@0 | 6769 | if (printSettingsAreGlobal) { |
michael@0 | 6770 | printSettingsService->GetGlobalPrintSettings(getter_AddRefs(printSettings)); |
michael@0 | 6771 | |
michael@0 | 6772 | nsXPIDLString printerName; |
michael@0 | 6773 | printSettings->GetPrinterName(getter_Copies(printerName)); |
michael@0 | 6774 | if (printerName.IsEmpty()) { |
michael@0 | 6775 | printSettingsService->GetDefaultPrinterName(getter_Copies(printerName)); |
michael@0 | 6776 | printSettings->SetPrinterName(printerName); |
michael@0 | 6777 | } |
michael@0 | 6778 | printSettingsService->InitPrintSettingsFromPrinter(printerName, printSettings); |
michael@0 | 6779 | printSettingsService->InitPrintSettingsFromPrefs(printSettings, |
michael@0 | 6780 | true, |
michael@0 | 6781 | nsIPrintSettings::kInitSaveAll); |
michael@0 | 6782 | } else { |
michael@0 | 6783 | printSettingsService->GetNewPrintSettings(getter_AddRefs(printSettings)); |
michael@0 | 6784 | } |
michael@0 | 6785 | |
michael@0 | 6786 | EnterModalState(); |
michael@0 | 6787 | webBrowserPrint->Print(printSettings, nullptr); |
michael@0 | 6788 | LeaveModalState(); |
michael@0 | 6789 | |
michael@0 | 6790 | bool savePrintSettings = |
michael@0 | 6791 | Preferences::GetBool("print.save_print_settings", false); |
michael@0 | 6792 | if (printSettingsAreGlobal && savePrintSettings) { |
michael@0 | 6793 | printSettingsService-> |
michael@0 | 6794 | SavePrintSettingsToPrefs(printSettings, |
michael@0 | 6795 | true, |
michael@0 | 6796 | nsIPrintSettings::kInitSaveAll); |
michael@0 | 6797 | printSettingsService-> |
michael@0 | 6798 | SavePrintSettingsToPrefs(printSettings, |
michael@0 | 6799 | false, |
michael@0 | 6800 | nsIPrintSettings::kInitSavePrinterName); |
michael@0 | 6801 | } |
michael@0 | 6802 | } else { |
michael@0 | 6803 | webBrowserPrint->GetGlobalPrintSettings(getter_AddRefs(printSettings)); |
michael@0 | 6804 | webBrowserPrint->Print(printSettings, nullptr); |
michael@0 | 6805 | } |
michael@0 | 6806 | } |
michael@0 | 6807 | #endif //NS_PRINTING |
michael@0 | 6808 | } |
michael@0 | 6809 | |
michael@0 | 6810 | NS_IMETHODIMP |
michael@0 | 6811 | nsGlobalWindow::Print() |
michael@0 | 6812 | { |
michael@0 | 6813 | ErrorResult rv; |
michael@0 | 6814 | Print(rv); |
michael@0 | 6815 | |
michael@0 | 6816 | return rv.ErrorCode(); |
michael@0 | 6817 | } |
michael@0 | 6818 | |
michael@0 | 6819 | void |
michael@0 | 6820 | nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos, ErrorResult& aError) |
michael@0 | 6821 | { |
michael@0 | 6822 | FORWARD_TO_OUTER_OR_THROW(MoveTo, (aXPos, aYPos, aError), aError, ); |
michael@0 | 6823 | |
michael@0 | 6824 | /* |
michael@0 | 6825 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 6826 | * prevent window.moveTo() by exiting early |
michael@0 | 6827 | */ |
michael@0 | 6828 | |
michael@0 | 6829 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 6830 | return; |
michael@0 | 6831 | } |
michael@0 | 6832 | |
michael@0 | 6833 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 6834 | if (!treeOwnerAsWin) { |
michael@0 | 6835 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6836 | return; |
michael@0 | 6837 | } |
michael@0 | 6838 | |
michael@0 | 6839 | // Mild abuse of a "size" object so we don't need more helper functions. |
michael@0 | 6840 | nsIntSize cssPos(aXPos, aYPos); |
michael@0 | 6841 | CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height); |
michael@0 | 6842 | |
michael@0 | 6843 | nsIntSize devPos = CSSToDevIntPixels(cssPos); |
michael@0 | 6844 | |
michael@0 | 6845 | aError = treeOwnerAsWin->SetPosition(devPos.width, devPos.height); |
michael@0 | 6846 | } |
michael@0 | 6847 | |
michael@0 | 6848 | NS_IMETHODIMP |
michael@0 | 6849 | nsGlobalWindow::MoveTo(int32_t aXPos, int32_t aYPos) |
michael@0 | 6850 | { |
michael@0 | 6851 | ErrorResult rv; |
michael@0 | 6852 | MoveTo(aXPos, aYPos, rv); |
michael@0 | 6853 | |
michael@0 | 6854 | return rv.ErrorCode(); |
michael@0 | 6855 | } |
michael@0 | 6856 | |
michael@0 | 6857 | void |
michael@0 | 6858 | nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif, ErrorResult& aError) |
michael@0 | 6859 | { |
michael@0 | 6860 | FORWARD_TO_OUTER_OR_THROW(MoveBy, (aXDif, aYDif, aError), aError, ); |
michael@0 | 6861 | |
michael@0 | 6862 | /* |
michael@0 | 6863 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 6864 | * prevent window.moveBy() by exiting early |
michael@0 | 6865 | */ |
michael@0 | 6866 | |
michael@0 | 6867 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 6868 | return; |
michael@0 | 6869 | } |
michael@0 | 6870 | |
michael@0 | 6871 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 6872 | if (!treeOwnerAsWin) { |
michael@0 | 6873 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6874 | return; |
michael@0 | 6875 | } |
michael@0 | 6876 | |
michael@0 | 6877 | // To do this correctly we have to convert what we get from GetPosition |
michael@0 | 6878 | // into CSS pixels, add the arguments, do the security check, and |
michael@0 | 6879 | // then convert back to device pixels for the call to SetPosition. |
michael@0 | 6880 | |
michael@0 | 6881 | int32_t x, y; |
michael@0 | 6882 | aError = treeOwnerAsWin->GetPosition(&x, &y); |
michael@0 | 6883 | if (aError.Failed()) { |
michael@0 | 6884 | return; |
michael@0 | 6885 | } |
michael@0 | 6886 | |
michael@0 | 6887 | // mild abuse of a "size" object so we don't need more helper functions |
michael@0 | 6888 | nsIntSize cssPos(DevToCSSIntPixels(nsIntSize(x, y))); |
michael@0 | 6889 | |
michael@0 | 6890 | cssPos.width += aXDif; |
michael@0 | 6891 | cssPos.height += aYDif; |
michael@0 | 6892 | |
michael@0 | 6893 | CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height); |
michael@0 | 6894 | |
michael@0 | 6895 | nsIntSize newDevPos(CSSToDevIntPixels(cssPos)); |
michael@0 | 6896 | |
michael@0 | 6897 | aError = treeOwnerAsWin->SetPosition(newDevPos.width, newDevPos.height); |
michael@0 | 6898 | } |
michael@0 | 6899 | |
michael@0 | 6900 | NS_IMETHODIMP |
michael@0 | 6901 | nsGlobalWindow::MoveBy(int32_t aXDif, int32_t aYDif) |
michael@0 | 6902 | { |
michael@0 | 6903 | ErrorResult rv; |
michael@0 | 6904 | MoveBy(aXDif, aYDif, rv); |
michael@0 | 6905 | |
michael@0 | 6906 | return rv.ErrorCode(); |
michael@0 | 6907 | } |
michael@0 | 6908 | |
michael@0 | 6909 | void |
michael@0 | 6910 | nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight, ErrorResult& aError) |
michael@0 | 6911 | { |
michael@0 | 6912 | FORWARD_TO_OUTER_OR_THROW(ResizeTo, (aWidth, aHeight, aError), aError, ); |
michael@0 | 6913 | |
michael@0 | 6914 | /* |
michael@0 | 6915 | * If caller is a browser-element then dispatch a resize event to |
michael@0 | 6916 | * the embedder. |
michael@0 | 6917 | */ |
michael@0 | 6918 | if (mDocShell && mDocShell->GetIsBrowserOrApp()) { |
michael@0 | 6919 | nsIntSize size(aWidth, aHeight); |
michael@0 | 6920 | if (!DispatchResizeEvent(size)) { |
michael@0 | 6921 | // The embedder chose to prevent the default action for this |
michael@0 | 6922 | // event, so let's not resize this window after all... |
michael@0 | 6923 | return; |
michael@0 | 6924 | } |
michael@0 | 6925 | } |
michael@0 | 6926 | |
michael@0 | 6927 | /* |
michael@0 | 6928 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 6929 | * prevent window.resizeTo() by exiting early |
michael@0 | 6930 | */ |
michael@0 | 6931 | |
michael@0 | 6932 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 6933 | return; |
michael@0 | 6934 | } |
michael@0 | 6935 | |
michael@0 | 6936 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 6937 | if (!treeOwnerAsWin) { |
michael@0 | 6938 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6939 | return; |
michael@0 | 6940 | } |
michael@0 | 6941 | |
michael@0 | 6942 | nsIntSize cssSize(aWidth, aHeight); |
michael@0 | 6943 | CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height); |
michael@0 | 6944 | |
michael@0 | 6945 | nsIntSize devSz(CSSToDevIntPixels(cssSize)); |
michael@0 | 6946 | |
michael@0 | 6947 | aError = treeOwnerAsWin->SetSize(devSz.width, devSz.height, true); |
michael@0 | 6948 | } |
michael@0 | 6949 | |
michael@0 | 6950 | NS_IMETHODIMP |
michael@0 | 6951 | nsGlobalWindow::ResizeTo(int32_t aWidth, int32_t aHeight) |
michael@0 | 6952 | { |
michael@0 | 6953 | ErrorResult rv; |
michael@0 | 6954 | ResizeTo(aWidth, aHeight, rv); |
michael@0 | 6955 | |
michael@0 | 6956 | return rv.ErrorCode(); |
michael@0 | 6957 | } |
michael@0 | 6958 | |
michael@0 | 6959 | void |
michael@0 | 6960 | nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif, |
michael@0 | 6961 | ErrorResult& aError) |
michael@0 | 6962 | { |
michael@0 | 6963 | FORWARD_TO_OUTER_OR_THROW(ResizeBy, (aWidthDif, aHeightDif, aError), aError, ); |
michael@0 | 6964 | |
michael@0 | 6965 | /* |
michael@0 | 6966 | * If caller is a browser-element then dispatch a resize event to |
michael@0 | 6967 | * parent. |
michael@0 | 6968 | */ |
michael@0 | 6969 | if (mDocShell && mDocShell->GetIsBrowserOrApp()) { |
michael@0 | 6970 | CSSIntSize size; |
michael@0 | 6971 | if (NS_FAILED(GetInnerSize(size))) { |
michael@0 | 6972 | return; |
michael@0 | 6973 | } |
michael@0 | 6974 | |
michael@0 | 6975 | size.width += aWidthDif; |
michael@0 | 6976 | size.height += aHeightDif; |
michael@0 | 6977 | |
michael@0 | 6978 | if (!DispatchResizeEvent(nsIntSize(size.width, size.height))) { |
michael@0 | 6979 | // The embedder chose to prevent the default action for this |
michael@0 | 6980 | // event, so let's not resize this window after all... |
michael@0 | 6981 | return; |
michael@0 | 6982 | } |
michael@0 | 6983 | } |
michael@0 | 6984 | |
michael@0 | 6985 | /* |
michael@0 | 6986 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 6987 | * prevent window.resizeBy() by exiting early |
michael@0 | 6988 | */ |
michael@0 | 6989 | |
michael@0 | 6990 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 6991 | return; |
michael@0 | 6992 | } |
michael@0 | 6993 | |
michael@0 | 6994 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 6995 | if (!treeOwnerAsWin) { |
michael@0 | 6996 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 6997 | return; |
michael@0 | 6998 | } |
michael@0 | 6999 | |
michael@0 | 7000 | int32_t width, height; |
michael@0 | 7001 | aError = treeOwnerAsWin->GetSize(&width, &height); |
michael@0 | 7002 | if (aError.Failed()) { |
michael@0 | 7003 | return; |
michael@0 | 7004 | } |
michael@0 | 7005 | |
michael@0 | 7006 | // To do this correctly we have to convert what we got from GetSize |
michael@0 | 7007 | // into CSS pixels, add the arguments, do the security check, and |
michael@0 | 7008 | // then convert back to device pixels for the call to SetSize. |
michael@0 | 7009 | |
michael@0 | 7010 | nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height))); |
michael@0 | 7011 | |
michael@0 | 7012 | cssSize.width += aWidthDif; |
michael@0 | 7013 | cssSize.height += aHeightDif; |
michael@0 | 7014 | |
michael@0 | 7015 | CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height); |
michael@0 | 7016 | |
michael@0 | 7017 | nsIntSize newDevSize(CSSToDevIntPixels(cssSize)); |
michael@0 | 7018 | |
michael@0 | 7019 | aError = treeOwnerAsWin->SetSize(newDevSize.width, newDevSize.height, true); |
michael@0 | 7020 | } |
michael@0 | 7021 | |
michael@0 | 7022 | NS_IMETHODIMP |
michael@0 | 7023 | nsGlobalWindow::ResizeBy(int32_t aWidthDif, int32_t aHeightDif) |
michael@0 | 7024 | { |
michael@0 | 7025 | ErrorResult rv; |
michael@0 | 7026 | ResizeBy(aWidthDif, aHeightDif, rv); |
michael@0 | 7027 | |
michael@0 | 7028 | return rv.ErrorCode(); |
michael@0 | 7029 | } |
michael@0 | 7030 | |
michael@0 | 7031 | void |
michael@0 | 7032 | nsGlobalWindow::SizeToContent(ErrorResult& aError) |
michael@0 | 7033 | { |
michael@0 | 7034 | FORWARD_TO_OUTER_OR_THROW(SizeToContent, (aError), aError, ); |
michael@0 | 7035 | |
michael@0 | 7036 | if (!mDocShell) { |
michael@0 | 7037 | return; |
michael@0 | 7038 | } |
michael@0 | 7039 | |
michael@0 | 7040 | /* |
michael@0 | 7041 | * If caller is not chrome and the user has not explicitly exempted the site, |
michael@0 | 7042 | * prevent window.sizeToContent() by exiting early |
michael@0 | 7043 | */ |
michael@0 | 7044 | |
michael@0 | 7045 | if (!CanMoveResizeWindows() || IsFrame()) { |
michael@0 | 7046 | return; |
michael@0 | 7047 | } |
michael@0 | 7048 | |
michael@0 | 7049 | // The content viewer does a check to make sure that it's a content |
michael@0 | 7050 | // viewer for a toplevel docshell. |
michael@0 | 7051 | nsCOMPtr<nsIContentViewer> cv; |
michael@0 | 7052 | mDocShell->GetContentViewer(getter_AddRefs(cv)); |
michael@0 | 7053 | nsCOMPtr<nsIMarkupDocumentViewer> markupViewer(do_QueryInterface(cv)); |
michael@0 | 7054 | if (!markupViewer) { |
michael@0 | 7055 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 7056 | return; |
michael@0 | 7057 | } |
michael@0 | 7058 | |
michael@0 | 7059 | int32_t width, height; |
michael@0 | 7060 | aError = markupViewer->GetContentSize(&width, &height); |
michael@0 | 7061 | if (aError.Failed()) { |
michael@0 | 7062 | return; |
michael@0 | 7063 | } |
michael@0 | 7064 | |
michael@0 | 7065 | // Make sure the new size is following the CheckSecurityWidthAndHeight |
michael@0 | 7066 | // rules. |
michael@0 | 7067 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); |
michael@0 | 7068 | if (!treeOwner) { |
michael@0 | 7069 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 7070 | return; |
michael@0 | 7071 | } |
michael@0 | 7072 | |
michael@0 | 7073 | nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height))); |
michael@0 | 7074 | CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height); |
michael@0 | 7075 | |
michael@0 | 7076 | nsIntSize newDevSize(CSSToDevIntPixels(cssSize)); |
michael@0 | 7077 | |
michael@0 | 7078 | aError = treeOwner->SizeShellTo(mDocShell, newDevSize.width, |
michael@0 | 7079 | newDevSize.height); |
michael@0 | 7080 | } |
michael@0 | 7081 | |
michael@0 | 7082 | NS_IMETHODIMP |
michael@0 | 7083 | nsGlobalWindow::SizeToContent() |
michael@0 | 7084 | { |
michael@0 | 7085 | ErrorResult rv; |
michael@0 | 7086 | SizeToContent(rv); |
michael@0 | 7087 | |
michael@0 | 7088 | return rv.ErrorCode(); |
michael@0 | 7089 | } |
michael@0 | 7090 | |
michael@0 | 7091 | NS_IMETHODIMP |
michael@0 | 7092 | nsGlobalWindow::GetWindowRoot(nsIDOMEventTarget **aWindowRoot) |
michael@0 | 7093 | { |
michael@0 | 7094 | nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot(); |
michael@0 | 7095 | return CallQueryInterface(root, aWindowRoot); |
michael@0 | 7096 | } |
michael@0 | 7097 | |
michael@0 | 7098 | already_AddRefed<nsPIWindowRoot> |
michael@0 | 7099 | nsGlobalWindow::GetTopWindowRoot() |
michael@0 | 7100 | { |
michael@0 | 7101 | nsPIDOMWindow* piWin = GetPrivateRoot(); |
michael@0 | 7102 | if (!piWin) { |
michael@0 | 7103 | return nullptr; |
michael@0 | 7104 | } |
michael@0 | 7105 | |
michael@0 | 7106 | nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler()); |
michael@0 | 7107 | return window.forget(); |
michael@0 | 7108 | } |
michael@0 | 7109 | |
michael@0 | 7110 | NS_IMETHODIMP |
michael@0 | 7111 | nsGlobalWindow::Scroll(int32_t aXScroll, int32_t aYScroll) |
michael@0 | 7112 | { |
michael@0 | 7113 | ScrollTo(CSSIntPoint(aXScroll, aYScroll)); |
michael@0 | 7114 | return NS_OK; |
michael@0 | 7115 | } |
michael@0 | 7116 | |
michael@0 | 7117 | NS_IMETHODIMP |
michael@0 | 7118 | nsGlobalWindow::ScrollTo(int32_t aXScroll, int32_t aYScroll) |
michael@0 | 7119 | { |
michael@0 | 7120 | ScrollTo(CSSIntPoint(aXScroll, aYScroll)); |
michael@0 | 7121 | return NS_OK; |
michael@0 | 7122 | } |
michael@0 | 7123 | |
michael@0 | 7124 | void |
michael@0 | 7125 | nsGlobalWindow::ScrollTo(const CSSIntPoint& aScroll) |
michael@0 | 7126 | { |
michael@0 | 7127 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 7128 | nsIScrollableFrame *sf = GetScrollFrame(); |
michael@0 | 7129 | |
michael@0 | 7130 | if (sf) { |
michael@0 | 7131 | // Here we calculate what the max pixel value is that we can |
michael@0 | 7132 | // scroll to, we do this by dividing maxint with the pixel to |
michael@0 | 7133 | // twips conversion factor, and subtracting 4, the 4 comes from |
michael@0 | 7134 | // experimenting with this value, anything less makes the view |
michael@0 | 7135 | // code not scroll correctly, I have no idea why. -- jst |
michael@0 | 7136 | const int32_t maxpx = nsPresContext::AppUnitsToIntCSSPixels(0x7fffffff) - 4; |
michael@0 | 7137 | |
michael@0 | 7138 | CSSIntPoint scroll(aScroll); |
michael@0 | 7139 | if (scroll.x > maxpx) { |
michael@0 | 7140 | scroll.x = maxpx; |
michael@0 | 7141 | } |
michael@0 | 7142 | |
michael@0 | 7143 | if (scroll.y > maxpx) { |
michael@0 | 7144 | scroll.y = maxpx; |
michael@0 | 7145 | } |
michael@0 | 7146 | sf->ScrollToCSSPixels(scroll); |
michael@0 | 7147 | } |
michael@0 | 7148 | } |
michael@0 | 7149 | |
michael@0 | 7150 | NS_IMETHODIMP |
michael@0 | 7151 | nsGlobalWindow::ScrollBy(int32_t aXScrollDif, int32_t aYScrollDif) |
michael@0 | 7152 | { |
michael@0 | 7153 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 7154 | nsIScrollableFrame *sf = GetScrollFrame(); |
michael@0 | 7155 | |
michael@0 | 7156 | if (sf) { |
michael@0 | 7157 | CSSIntPoint scrollPos = |
michael@0 | 7158 | sf->GetScrollPositionCSSPixels() + CSSIntPoint(aXScrollDif, aYScrollDif); |
michael@0 | 7159 | // It seems like it would make more sense for ScrollBy to use |
michael@0 | 7160 | // SMOOTH mode, but tests seem to depend on the synchronous behaviour. |
michael@0 | 7161 | // Perhaps Web content does too. |
michael@0 | 7162 | ScrollTo(scrollPos); |
michael@0 | 7163 | } |
michael@0 | 7164 | |
michael@0 | 7165 | return NS_OK; |
michael@0 | 7166 | } |
michael@0 | 7167 | |
michael@0 | 7168 | NS_IMETHODIMP |
michael@0 | 7169 | nsGlobalWindow::ScrollByLines(int32_t numLines) |
michael@0 | 7170 | { |
michael@0 | 7171 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 7172 | nsIScrollableFrame *sf = GetScrollFrame(); |
michael@0 | 7173 | if (sf) { |
michael@0 | 7174 | // It seems like it would make more sense for ScrollByLines to use |
michael@0 | 7175 | // SMOOTH mode, but tests seem to depend on the synchronous behaviour. |
michael@0 | 7176 | // Perhaps Web content does too. |
michael@0 | 7177 | sf->ScrollBy(nsIntPoint(0, numLines), nsIScrollableFrame::LINES, |
michael@0 | 7178 | nsIScrollableFrame::INSTANT); |
michael@0 | 7179 | } |
michael@0 | 7180 | |
michael@0 | 7181 | return NS_OK; |
michael@0 | 7182 | } |
michael@0 | 7183 | |
michael@0 | 7184 | NS_IMETHODIMP |
michael@0 | 7185 | nsGlobalWindow::ScrollByPages(int32_t numPages) |
michael@0 | 7186 | { |
michael@0 | 7187 | FlushPendingNotifications(Flush_Layout); |
michael@0 | 7188 | nsIScrollableFrame *sf = GetScrollFrame(); |
michael@0 | 7189 | if (sf) { |
michael@0 | 7190 | // It seems like it would make more sense for ScrollByPages to use |
michael@0 | 7191 | // SMOOTH mode, but tests seem to depend on the synchronous behaviour. |
michael@0 | 7192 | // Perhaps Web content does too. |
michael@0 | 7193 | sf->ScrollBy(nsIntPoint(0, numPages), nsIScrollableFrame::PAGES, |
michael@0 | 7194 | nsIScrollableFrame::INSTANT); |
michael@0 | 7195 | } |
michael@0 | 7196 | |
michael@0 | 7197 | return NS_OK; |
michael@0 | 7198 | } |
michael@0 | 7199 | |
michael@0 | 7200 | void |
michael@0 | 7201 | nsGlobalWindow::ClearTimeout(int32_t aHandle, ErrorResult& aError) |
michael@0 | 7202 | { |
michael@0 | 7203 | if (aHandle > 0) { |
michael@0 | 7204 | ClearTimeoutOrInterval(aHandle, aError); |
michael@0 | 7205 | } |
michael@0 | 7206 | } |
michael@0 | 7207 | |
michael@0 | 7208 | NS_IMETHODIMP |
michael@0 | 7209 | nsGlobalWindow::ClearTimeout(int32_t aHandle) |
michael@0 | 7210 | { |
michael@0 | 7211 | ErrorResult rv; |
michael@0 | 7212 | ClearTimeout(aHandle, rv); |
michael@0 | 7213 | |
michael@0 | 7214 | return rv.ErrorCode(); |
michael@0 | 7215 | } |
michael@0 | 7216 | |
michael@0 | 7217 | void |
michael@0 | 7218 | nsGlobalWindow::ClearInterval(int32_t aHandle, ErrorResult& aError) |
michael@0 | 7219 | { |
michael@0 | 7220 | if (aHandle > 0) { |
michael@0 | 7221 | ClearTimeoutOrInterval(aHandle, aError); |
michael@0 | 7222 | } |
michael@0 | 7223 | } |
michael@0 | 7224 | |
michael@0 | 7225 | NS_IMETHODIMP |
michael@0 | 7226 | nsGlobalWindow::ClearInterval(int32_t aHandle) |
michael@0 | 7227 | { |
michael@0 | 7228 | ErrorResult rv; |
michael@0 | 7229 | ClearInterval(aHandle, rv); |
michael@0 | 7230 | |
michael@0 | 7231 | return rv.ErrorCode(); |
michael@0 | 7232 | } |
michael@0 | 7233 | |
michael@0 | 7234 | NS_IMETHODIMP |
michael@0 | 7235 | nsGlobalWindow::SetTimeout(int32_t *_retval) |
michael@0 | 7236 | { |
michael@0 | 7237 | return SetTimeoutOrInterval(false, _retval); |
michael@0 | 7238 | } |
michael@0 | 7239 | |
michael@0 | 7240 | NS_IMETHODIMP |
michael@0 | 7241 | nsGlobalWindow::SetInterval(int32_t *_retval) |
michael@0 | 7242 | { |
michael@0 | 7243 | return SetTimeoutOrInterval(true, _retval); |
michael@0 | 7244 | } |
michael@0 | 7245 | |
michael@0 | 7246 | NS_IMETHODIMP |
michael@0 | 7247 | nsGlobalWindow::SetResizable(bool aResizable) |
michael@0 | 7248 | { |
michael@0 | 7249 | // nop |
michael@0 | 7250 | |
michael@0 | 7251 | return NS_OK; |
michael@0 | 7252 | } |
michael@0 | 7253 | |
michael@0 | 7254 | NS_IMETHODIMP |
michael@0 | 7255 | nsGlobalWindow::CaptureEvents() |
michael@0 | 7256 | { |
michael@0 | 7257 | if (mDoc) { |
michael@0 | 7258 | mDoc->WarnOnceAbout(nsIDocument::eUseOfCaptureEvents); |
michael@0 | 7259 | } |
michael@0 | 7260 | |
michael@0 | 7261 | return NS_OK; |
michael@0 | 7262 | } |
michael@0 | 7263 | |
michael@0 | 7264 | NS_IMETHODIMP |
michael@0 | 7265 | nsGlobalWindow::ReleaseEvents() |
michael@0 | 7266 | { |
michael@0 | 7267 | if (mDoc) { |
michael@0 | 7268 | mDoc->WarnOnceAbout(nsIDocument::eUseOfReleaseEvents); |
michael@0 | 7269 | } |
michael@0 | 7270 | |
michael@0 | 7271 | return NS_OK; |
michael@0 | 7272 | } |
michael@0 | 7273 | |
michael@0 | 7274 | static |
michael@0 | 7275 | bool IsPopupBlocked(nsIDocument* aDoc) |
michael@0 | 7276 | { |
michael@0 | 7277 | nsCOMPtr<nsIPopupWindowManager> pm = |
michael@0 | 7278 | do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID); |
michael@0 | 7279 | |
michael@0 | 7280 | if (!pm) { |
michael@0 | 7281 | return false; |
michael@0 | 7282 | } |
michael@0 | 7283 | |
michael@0 | 7284 | if (!aDoc) { |
michael@0 | 7285 | return true; |
michael@0 | 7286 | } |
michael@0 | 7287 | |
michael@0 | 7288 | uint32_t permission = nsIPopupWindowManager::ALLOW_POPUP; |
michael@0 | 7289 | pm->TestPermission(aDoc->NodePrincipal(), &permission); |
michael@0 | 7290 | return permission == nsIPopupWindowManager::DENY_POPUP; |
michael@0 | 7291 | } |
michael@0 | 7292 | |
michael@0 | 7293 | /* static */ |
michael@0 | 7294 | void |
michael@0 | 7295 | nsGlobalWindow::FirePopupBlockedEvent(nsIDocument* aDoc, |
michael@0 | 7296 | nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI, |
michael@0 | 7297 | const nsAString &aPopupWindowName, |
michael@0 | 7298 | const nsAString &aPopupWindowFeatures) |
michael@0 | 7299 | { |
michael@0 | 7300 | if (aDoc) { |
michael@0 | 7301 | // Fire a "DOMPopupBlocked" event so that the UI can hear about |
michael@0 | 7302 | // blocked popups. |
michael@0 | 7303 | nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDoc); |
michael@0 | 7304 | nsCOMPtr<nsIDOMEvent> event; |
michael@0 | 7305 | doc->CreateEvent(NS_LITERAL_STRING("PopupBlockedEvents"), getter_AddRefs(event)); |
michael@0 | 7306 | if (event) { |
michael@0 | 7307 | nsCOMPtr<nsIDOMPopupBlockedEvent> pbev(do_QueryInterface(event)); |
michael@0 | 7308 | pbev->InitPopupBlockedEvent(NS_LITERAL_STRING("DOMPopupBlocked"), |
michael@0 | 7309 | true, true, aRequestingWindow, |
michael@0 | 7310 | aPopupURI, aPopupWindowName, |
michael@0 | 7311 | aPopupWindowFeatures); |
michael@0 | 7312 | event->SetTrusted(true); |
michael@0 | 7313 | |
michael@0 | 7314 | bool defaultActionEnabled; |
michael@0 | 7315 | aDoc->DispatchEvent(event, &defaultActionEnabled); |
michael@0 | 7316 | } |
michael@0 | 7317 | } |
michael@0 | 7318 | } |
michael@0 | 7319 | |
michael@0 | 7320 | static void FirePopupWindowEvent(nsIDocument* aDoc) |
michael@0 | 7321 | { |
michael@0 | 7322 | // Fire a "PopupWindow" event |
michael@0 | 7323 | nsContentUtils::DispatchTrustedEvent(aDoc, aDoc, |
michael@0 | 7324 | NS_LITERAL_STRING("PopupWindow"), |
michael@0 | 7325 | true, true); |
michael@0 | 7326 | } |
michael@0 | 7327 | |
michael@0 | 7328 | // static |
michael@0 | 7329 | bool |
michael@0 | 7330 | nsGlobalWindow::CanSetProperty(const char *aPrefName) |
michael@0 | 7331 | { |
michael@0 | 7332 | // Chrome can set any property. |
michael@0 | 7333 | if (nsContentUtils::IsCallerChrome()) { |
michael@0 | 7334 | return true; |
michael@0 | 7335 | } |
michael@0 | 7336 | |
michael@0 | 7337 | // If the pref is set to true, we can not set the property |
michael@0 | 7338 | // and vice versa. |
michael@0 | 7339 | return !Preferences::GetBool(aPrefName, true); |
michael@0 | 7340 | } |
michael@0 | 7341 | |
michael@0 | 7342 | bool |
michael@0 | 7343 | nsGlobalWindow::PopupWhitelisted() |
michael@0 | 7344 | { |
michael@0 | 7345 | if (!IsPopupBlocked(mDoc)) |
michael@0 | 7346 | return true; |
michael@0 | 7347 | |
michael@0 | 7348 | nsCOMPtr<nsIDOMWindow> parent; |
michael@0 | 7349 | |
michael@0 | 7350 | if (NS_FAILED(GetParent(getter_AddRefs(parent))) || |
michael@0 | 7351 | parent == static_cast<nsIDOMWindow*>(this)) |
michael@0 | 7352 | { |
michael@0 | 7353 | return false; |
michael@0 | 7354 | } |
michael@0 | 7355 | |
michael@0 | 7356 | return static_cast<nsGlobalWindow*> |
michael@0 | 7357 | (static_cast<nsIDOMWindow*> |
michael@0 | 7358 | (parent.get()))->PopupWhitelisted(); |
michael@0 | 7359 | } |
michael@0 | 7360 | |
michael@0 | 7361 | /* |
michael@0 | 7362 | * Examine the current document state to see if we're in a way that is |
michael@0 | 7363 | * typically abused by web designers. The window.open code uses this |
michael@0 | 7364 | * routine to determine whether to allow the new window. |
michael@0 | 7365 | * Returns a value from the PopupControlState enum. |
michael@0 | 7366 | */ |
michael@0 | 7367 | PopupControlState |
michael@0 | 7368 | nsGlobalWindow::RevisePopupAbuseLevel(PopupControlState aControl) |
michael@0 | 7369 | { |
michael@0 | 7370 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 7371 | |
michael@0 | 7372 | NS_ASSERTION(mDocShell, "Must have docshell"); |
michael@0 | 7373 | |
michael@0 | 7374 | if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) { |
michael@0 | 7375 | return openAllowed; |
michael@0 | 7376 | } |
michael@0 | 7377 | |
michael@0 | 7378 | PopupControlState abuse = aControl; |
michael@0 | 7379 | switch (abuse) { |
michael@0 | 7380 | case openControlled: |
michael@0 | 7381 | case openAbused: |
michael@0 | 7382 | case openOverridden: |
michael@0 | 7383 | if (PopupWhitelisted()) |
michael@0 | 7384 | abuse = PopupControlState(abuse - 1); |
michael@0 | 7385 | case openAllowed: break; |
michael@0 | 7386 | default: |
michael@0 | 7387 | NS_WARNING("Strange PopupControlState!"); |
michael@0 | 7388 | } |
michael@0 | 7389 | |
michael@0 | 7390 | // limit the number of simultaneously open popups |
michael@0 | 7391 | if (abuse == openAbused || abuse == openControlled) { |
michael@0 | 7392 | int32_t popupMax = Preferences::GetInt("dom.popup_maximum", -1); |
michael@0 | 7393 | if (popupMax >= 0 && gOpenPopupSpamCount >= popupMax) |
michael@0 | 7394 | abuse = openOverridden; |
michael@0 | 7395 | } |
michael@0 | 7396 | |
michael@0 | 7397 | return abuse; |
michael@0 | 7398 | } |
michael@0 | 7399 | |
michael@0 | 7400 | /* If a window open is blocked, fire the appropriate DOM events. |
michael@0 | 7401 | aBlocked signifies we just blocked a popup. |
michael@0 | 7402 | aWindow signifies we just opened what is probably a popup. |
michael@0 | 7403 | */ |
michael@0 | 7404 | void |
michael@0 | 7405 | nsGlobalWindow::FireAbuseEvents(bool aBlocked, bool aWindow, |
michael@0 | 7406 | const nsAString &aPopupURL, |
michael@0 | 7407 | const nsAString &aPopupWindowName, |
michael@0 | 7408 | const nsAString &aPopupWindowFeatures) |
michael@0 | 7409 | { |
michael@0 | 7410 | // fetch the URI of the window requesting the opened window |
michael@0 | 7411 | |
michael@0 | 7412 | nsCOMPtr<nsIDOMWindow> topWindow; |
michael@0 | 7413 | GetTop(getter_AddRefs(topWindow)); |
michael@0 | 7414 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(topWindow); |
michael@0 | 7415 | if (!window) { |
michael@0 | 7416 | return; |
michael@0 | 7417 | } |
michael@0 | 7418 | |
michael@0 | 7419 | nsCOMPtr<nsIDocument> topDoc = window->GetDoc(); |
michael@0 | 7420 | nsCOMPtr<nsIURI> popupURI; |
michael@0 | 7421 | |
michael@0 | 7422 | // build the URI of the would-have-been popup window |
michael@0 | 7423 | // (see nsWindowWatcher::URIfromURL) |
michael@0 | 7424 | |
michael@0 | 7425 | // first, fetch the opener's base URI |
michael@0 | 7426 | |
michael@0 | 7427 | nsIURI *baseURL = nullptr; |
michael@0 | 7428 | |
michael@0 | 7429 | JSContext *cx = nsContentUtils::GetCurrentJSContext(); |
michael@0 | 7430 | nsCOMPtr<nsPIDOMWindow> contextWindow; |
michael@0 | 7431 | |
michael@0 | 7432 | if (cx) { |
michael@0 | 7433 | nsIScriptContext *currentCX = nsJSUtils::GetDynamicScriptContext(cx); |
michael@0 | 7434 | if (currentCX) { |
michael@0 | 7435 | contextWindow = do_QueryInterface(currentCX->GetGlobalObject()); |
michael@0 | 7436 | } |
michael@0 | 7437 | } |
michael@0 | 7438 | if (!contextWindow) { |
michael@0 | 7439 | contextWindow = this; |
michael@0 | 7440 | } |
michael@0 | 7441 | |
michael@0 | 7442 | nsCOMPtr<nsIDocument> doc = contextWindow->GetDoc(); |
michael@0 | 7443 | if (doc) |
michael@0 | 7444 | baseURL = doc->GetDocBaseURI(); |
michael@0 | 7445 | |
michael@0 | 7446 | // use the base URI to build what would have been the popup's URI |
michael@0 | 7447 | nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID)); |
michael@0 | 7448 | if (ios) |
michael@0 | 7449 | ios->NewURI(NS_ConvertUTF16toUTF8(aPopupURL), 0, baseURL, |
michael@0 | 7450 | getter_AddRefs(popupURI)); |
michael@0 | 7451 | |
michael@0 | 7452 | // fire an event chock full of informative URIs |
michael@0 | 7453 | if (aBlocked) { |
michael@0 | 7454 | FirePopupBlockedEvent(topDoc, this, popupURI, aPopupWindowName, |
michael@0 | 7455 | aPopupWindowFeatures); |
michael@0 | 7456 | } |
michael@0 | 7457 | if (aWindow) |
michael@0 | 7458 | FirePopupWindowEvent(topDoc); |
michael@0 | 7459 | } |
michael@0 | 7460 | |
michael@0 | 7461 | already_AddRefed<nsIDOMWindow> |
michael@0 | 7462 | nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName, |
michael@0 | 7463 | const nsAString& aOptions, ErrorResult& aError) |
michael@0 | 7464 | { |
michael@0 | 7465 | nsCOMPtr<nsIDOMWindow> window; |
michael@0 | 7466 | aError = OpenJS(aUrl, aName, aOptions, getter_AddRefs(window)); |
michael@0 | 7467 | return window.forget(); |
michael@0 | 7468 | } |
michael@0 | 7469 | |
michael@0 | 7470 | NS_IMETHODIMP |
michael@0 | 7471 | nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName, |
michael@0 | 7472 | const nsAString& aOptions, nsIDOMWindow **_retval) |
michael@0 | 7473 | { |
michael@0 | 7474 | return OpenInternal(aUrl, aName, aOptions, |
michael@0 | 7475 | false, // aDialog |
michael@0 | 7476 | false, // aContentModal |
michael@0 | 7477 | true, // aCalledNoScript |
michael@0 | 7478 | false, // aDoJSFixups |
michael@0 | 7479 | true, // aNavigate |
michael@0 | 7480 | nullptr, nullptr, // No args |
michael@0 | 7481 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 7482 | nullptr, // aJSCallerContext |
michael@0 | 7483 | _retval); |
michael@0 | 7484 | } |
michael@0 | 7485 | |
michael@0 | 7486 | NS_IMETHODIMP |
michael@0 | 7487 | nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName, |
michael@0 | 7488 | const nsAString& aOptions, nsIDOMWindow **_retval) |
michael@0 | 7489 | { |
michael@0 | 7490 | return OpenInternal(aUrl, aName, aOptions, |
michael@0 | 7491 | false, // aDialog |
michael@0 | 7492 | false, // aContentModal |
michael@0 | 7493 | false, // aCalledNoScript |
michael@0 | 7494 | true, // aDoJSFixups |
michael@0 | 7495 | true, // aNavigate |
michael@0 | 7496 | nullptr, nullptr, // No args |
michael@0 | 7497 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 7498 | nsContentUtils::GetCurrentJSContext(), // aJSCallerContext |
michael@0 | 7499 | _retval); |
michael@0 | 7500 | } |
michael@0 | 7501 | |
michael@0 | 7502 | // like Open, but attaches to the new window any extra parameters past |
michael@0 | 7503 | // [features] as a JS property named "arguments" |
michael@0 | 7504 | NS_IMETHODIMP |
michael@0 | 7505 | nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName, |
michael@0 | 7506 | const nsAString& aOptions, |
michael@0 | 7507 | nsISupports* aExtraArgument, nsIDOMWindow** _retval) |
michael@0 | 7508 | { |
michael@0 | 7509 | return OpenInternal(aUrl, aName, aOptions, |
michael@0 | 7510 | true, // aDialog |
michael@0 | 7511 | false, // aContentModal |
michael@0 | 7512 | true, // aCalledNoScript |
michael@0 | 7513 | false, // aDoJSFixups |
michael@0 | 7514 | true, // aNavigate |
michael@0 | 7515 | nullptr, aExtraArgument, // Arguments |
michael@0 | 7516 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 7517 | nullptr, // aJSCallerContext |
michael@0 | 7518 | _retval); |
michael@0 | 7519 | } |
michael@0 | 7520 | |
michael@0 | 7521 | // Like Open, but passes aNavigate=false. |
michael@0 | 7522 | /* virtual */ nsresult |
michael@0 | 7523 | nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl, |
michael@0 | 7524 | const nsAString& aName, |
michael@0 | 7525 | const nsAString& aOptions, |
michael@0 | 7526 | nsIDOMWindow **_retval) |
michael@0 | 7527 | { |
michael@0 | 7528 | return OpenInternal(aUrl, aName, aOptions, |
michael@0 | 7529 | false, // aDialog |
michael@0 | 7530 | false, // aContentModal |
michael@0 | 7531 | true, // aCalledNoScript |
michael@0 | 7532 | false, // aDoJSFixups |
michael@0 | 7533 | false, // aNavigate |
michael@0 | 7534 | nullptr, nullptr, // No args |
michael@0 | 7535 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 7536 | nullptr, // aJSCallerContext |
michael@0 | 7537 | _retval); |
michael@0 | 7538 | |
michael@0 | 7539 | } |
michael@0 | 7540 | |
michael@0 | 7541 | already_AddRefed<nsIDOMWindow> |
michael@0 | 7542 | nsGlobalWindow::OpenDialog(JSContext* aCx, const nsAString& aUrl, |
michael@0 | 7543 | const nsAString& aName, const nsAString& aOptions, |
michael@0 | 7544 | const Sequence<JS::Value>& aExtraArgument, |
michael@0 | 7545 | ErrorResult& aError) |
michael@0 | 7546 | { |
michael@0 | 7547 | nsCOMPtr<nsIJSArgArray> argvArray; |
michael@0 | 7548 | aError = NS_CreateJSArgv(aCx, aExtraArgument.Length(), |
michael@0 | 7549 | const_cast<JS::Value*>(aExtraArgument.Elements()), |
michael@0 | 7550 | getter_AddRefs(argvArray)); |
michael@0 | 7551 | if (aError.Failed()) { |
michael@0 | 7552 | return nullptr; |
michael@0 | 7553 | } |
michael@0 | 7554 | |
michael@0 | 7555 | nsCOMPtr<nsIDOMWindow> dialog; |
michael@0 | 7556 | aError = OpenInternal(aUrl, aName, aOptions, |
michael@0 | 7557 | true, // aDialog |
michael@0 | 7558 | false, // aContentModal |
michael@0 | 7559 | false, // aCalledNoScript |
michael@0 | 7560 | false, // aDoJSFixups |
michael@0 | 7561 | true, // aNavigate |
michael@0 | 7562 | argvArray, nullptr, // Arguments |
michael@0 | 7563 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 7564 | aCx, // aJSCallerContext |
michael@0 | 7565 | getter_AddRefs(dialog)); |
michael@0 | 7566 | return dialog.forget(); |
michael@0 | 7567 | } |
michael@0 | 7568 | |
michael@0 | 7569 | NS_IMETHODIMP |
michael@0 | 7570 | nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName, |
michael@0 | 7571 | const nsAString& aOptions, nsIDOMWindow** _retval) |
michael@0 | 7572 | { |
michael@0 | 7573 | if (!nsContentUtils::IsCallerChrome()) { |
michael@0 | 7574 | return NS_ERROR_DOM_SECURITY_ERR; |
michael@0 | 7575 | } |
michael@0 | 7576 | |
michael@0 | 7577 | nsAXPCNativeCallContext *ncc = nullptr; |
michael@0 | 7578 | nsresult rv = nsContentUtils::XPConnect()-> |
michael@0 | 7579 | GetCurrentNativeCallContext(&ncc); |
michael@0 | 7580 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 7581 | |
michael@0 | 7582 | if (!ncc) |
michael@0 | 7583 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 7584 | |
michael@0 | 7585 | JSContext *cx = nullptr; |
michael@0 | 7586 | |
michael@0 | 7587 | rv = ncc->GetJSContext(&cx); |
michael@0 | 7588 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 7589 | |
michael@0 | 7590 | uint32_t argc; |
michael@0 | 7591 | JS::Value *argv = nullptr; |
michael@0 | 7592 | |
michael@0 | 7593 | // XXX - need to get this as nsISupports? |
michael@0 | 7594 | ncc->GetArgc(&argc); |
michael@0 | 7595 | ncc->GetArgvPtr(&argv); |
michael@0 | 7596 | |
michael@0 | 7597 | // Strip the url, name and options from the args seen by scripts. |
michael@0 | 7598 | uint32_t argOffset = argc < 3 ? argc : 3; |
michael@0 | 7599 | nsCOMPtr<nsIJSArgArray> argvArray; |
michael@0 | 7600 | rv = NS_CreateJSArgv(cx, argc - argOffset, argv + argOffset, |
michael@0 | 7601 | getter_AddRefs(argvArray)); |
michael@0 | 7602 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 7603 | |
michael@0 | 7604 | return OpenInternal(aUrl, aName, aOptions, |
michael@0 | 7605 | true, // aDialog |
michael@0 | 7606 | false, // aContentModal |
michael@0 | 7607 | false, // aCalledNoScript |
michael@0 | 7608 | false, // aDoJSFixups |
michael@0 | 7609 | true, // aNavigate |
michael@0 | 7610 | argvArray, nullptr, // Arguments |
michael@0 | 7611 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 7612 | cx, // aJSCallerContext |
michael@0 | 7613 | _retval); |
michael@0 | 7614 | } |
michael@0 | 7615 | |
michael@0 | 7616 | already_AddRefed<nsIDOMWindow> |
michael@0 | 7617 | nsGlobalWindow::GetFrames(ErrorResult& aError) |
michael@0 | 7618 | { |
michael@0 | 7619 | FORWARD_TO_OUTER_OR_THROW(GetFrames, (aError), aError, nullptr); |
michael@0 | 7620 | |
michael@0 | 7621 | nsRefPtr<nsGlobalWindow> frames(this); |
michael@0 | 7622 | FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 7623 | return frames.forget(); |
michael@0 | 7624 | } |
michael@0 | 7625 | |
michael@0 | 7626 | NS_IMETHODIMP |
michael@0 | 7627 | nsGlobalWindow::GetFrames(nsIDOMWindow** aFrames) |
michael@0 | 7628 | { |
michael@0 | 7629 | ErrorResult rv; |
michael@0 | 7630 | nsCOMPtr<nsIDOMWindow> frames = GetFrames(rv); |
michael@0 | 7631 | frames.forget(aFrames); |
michael@0 | 7632 | |
michael@0 | 7633 | return rv.ErrorCode(); |
michael@0 | 7634 | } |
michael@0 | 7635 | |
michael@0 | 7636 | JSObject* nsGlobalWindow::CallerGlobal() |
michael@0 | 7637 | { |
michael@0 | 7638 | JSContext *cx = nsContentUtils::GetCurrentJSContext(); |
michael@0 | 7639 | if (!cx) { |
michael@0 | 7640 | NS_ERROR("Please don't call this method from C++!"); |
michael@0 | 7641 | |
michael@0 | 7642 | return nullptr; |
michael@0 | 7643 | } |
michael@0 | 7644 | |
michael@0 | 7645 | // If somebody does sameOriginIframeWindow.postMessage(...), they probably |
michael@0 | 7646 | // expect the .source attribute of the resulting message event to be |window| |
michael@0 | 7647 | // rather than |sameOriginIframeWindow|, even though the transparent wrapper |
michael@0 | 7648 | // semantics of same-origin access will cause us to be in the iframe's |
michael@0 | 7649 | // compartment at the time of the call. This means that we want the incumbent |
michael@0 | 7650 | // global here, rather than the global of the current compartment. |
michael@0 | 7651 | // |
michael@0 | 7652 | // There are various edge cases in which the incumbent global and the current |
michael@0 | 7653 | // global would not be same-origin. They include: |
michael@0 | 7654 | // * A privileged caller (System Principal or Expanded Principal) manipulating |
michael@0 | 7655 | // less-privileged content via Xray Waivers. |
michael@0 | 7656 | // * An unprivileged caller invoking a cross-origin function that was exposed |
michael@0 | 7657 | // to it by privileged code (i.e. Sandbox.importFunction). |
michael@0 | 7658 | // |
michael@0 | 7659 | // In these cases, we probably don't want the privileged global appearing in the |
michael@0 | 7660 | // .source attribute. So we fall back to the compartment global there. |
michael@0 | 7661 | JS::Rooted<JSObject*> incumbentGlobal(cx, &IncumbentJSGlobal()); |
michael@0 | 7662 | JS::Rooted<JSObject*> compartmentGlobal(cx, JS::CurrentGlobalOrNull(cx)); |
michael@0 | 7663 | nsIPrincipal* incumbentPrin = nsContentUtils::GetObjectPrincipal(incumbentGlobal); |
michael@0 | 7664 | nsIPrincipal* compartmentPrin = nsContentUtils::GetObjectPrincipal(compartmentGlobal); |
michael@0 | 7665 | return incumbentPrin->EqualsConsideringDomain(compartmentPrin) ? incumbentGlobal |
michael@0 | 7666 | : compartmentGlobal; |
michael@0 | 7667 | } |
michael@0 | 7668 | |
michael@0 | 7669 | |
michael@0 | 7670 | nsGlobalWindow* |
michael@0 | 7671 | nsGlobalWindow::CallerInnerWindow() |
michael@0 | 7672 | { |
michael@0 | 7673 | JSContext *cx = nsContentUtils::GetCurrentJSContext(); |
michael@0 | 7674 | NS_ENSURE_TRUE(cx, nullptr); |
michael@0 | 7675 | JS::Rooted<JSObject*> scope(cx, CallerGlobal()); |
michael@0 | 7676 | |
michael@0 | 7677 | // When Jetpack runs content scripts inside a sandbox, it uses |
michael@0 | 7678 | // sandboxPrototype to make them appear as though they're running in the |
michael@0 | 7679 | // scope of the page. So when a content script invokes postMessage, it expects |
michael@0 | 7680 | // the |source| of the received message to be the window set as the |
michael@0 | 7681 | // sandboxPrototype. This used to work incidentally for unrelated reasons, but |
michael@0 | 7682 | // now we need to do some special handling to support it. |
michael@0 | 7683 | { |
michael@0 | 7684 | JSAutoCompartment ac(cx, scope); |
michael@0 | 7685 | JS::Rooted<JSObject*> scopeProto(cx); |
michael@0 | 7686 | bool ok = JS_GetPrototype(cx, scope, &scopeProto); |
michael@0 | 7687 | NS_ENSURE_TRUE(ok, nullptr); |
michael@0 | 7688 | if (scopeProto && xpc::IsSandboxPrototypeProxy(scopeProto) && |
michael@0 | 7689 | (scopeProto = js::CheckedUnwrap(scopeProto, /* stopAtOuter = */ false))) |
michael@0 | 7690 | { |
michael@0 | 7691 | scope = scopeProto; |
michael@0 | 7692 | } |
michael@0 | 7693 | } |
michael@0 | 7694 | JSAutoCompartment ac(cx, scope); |
michael@0 | 7695 | |
michael@0 | 7696 | // We don't use xpc::WindowOrNull here because we want to be able to tell |
michael@0 | 7697 | // apart the cases of "scope is not an nsISupports at all" and "scope is an |
michael@0 | 7698 | // nsISupports that's not a window". It's not clear whether that's desirable, |
michael@0 | 7699 | // see bug 984467. |
michael@0 | 7700 | nsISupports* native = |
michael@0 | 7701 | nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, scope); |
michael@0 | 7702 | if (!native) |
michael@0 | 7703 | return nullptr; |
michael@0 | 7704 | |
michael@0 | 7705 | // The calling window must be holding a reference, so we can just return a |
michael@0 | 7706 | // raw pointer here and let the QI's addref be balanced by the nsCOMPtr |
michael@0 | 7707 | // destructor's release. |
michael@0 | 7708 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(native); |
michael@0 | 7709 | if (!win) |
michael@0 | 7710 | return GetCurrentInnerWindowInternal(); |
michael@0 | 7711 | return static_cast<nsGlobalWindow*>(win.get()); |
michael@0 | 7712 | } |
michael@0 | 7713 | |
michael@0 | 7714 | nsresult |
michael@0 | 7715 | nsGlobalWindow::GetFirstPartyIsolationURI(nsIURI** aFirstPartyIsolationURI) |
michael@0 | 7716 | { |
michael@0 | 7717 | nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = |
michael@0 | 7718 | do_GetService(THIRDPARTYUTIL_CONTRACTID); |
michael@0 | 7719 | if (!thirdPartyUtil) |
michael@0 | 7720 | return NS_ERROR_FAILURE; |
michael@0 | 7721 | |
michael@0 | 7722 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(mDoc); |
michael@0 | 7723 | return thirdPartyUtil->GetFirstPartyIsolationURI(NULL, doc, aFirstPartyIsolationURI); |
michael@0 | 7724 | } |
michael@0 | 7725 | |
michael@0 | 7726 | |
michael@0 | 7727 | /** |
michael@0 | 7728 | * Class used to represent events generated by calls to Window.postMessage, |
michael@0 | 7729 | * which asynchronously creates and dispatches events. |
michael@0 | 7730 | */ |
michael@0 | 7731 | class PostMessageEvent : public nsRunnable |
michael@0 | 7732 | { |
michael@0 | 7733 | public: |
michael@0 | 7734 | NS_DECL_NSIRUNNABLE |
michael@0 | 7735 | |
michael@0 | 7736 | PostMessageEvent(nsGlobalWindow* aSource, |
michael@0 | 7737 | const nsAString& aCallerOrigin, |
michael@0 | 7738 | nsGlobalWindow* aTargetWindow, |
michael@0 | 7739 | nsIPrincipal* aProvidedPrincipal, |
michael@0 | 7740 | bool aTrustedCaller) |
michael@0 | 7741 | : mSource(aSource), |
michael@0 | 7742 | mCallerOrigin(aCallerOrigin), |
michael@0 | 7743 | mTargetWindow(aTargetWindow), |
michael@0 | 7744 | mProvidedPrincipal(aProvidedPrincipal), |
michael@0 | 7745 | mTrustedCaller(aTrustedCaller) |
michael@0 | 7746 | { |
michael@0 | 7747 | MOZ_COUNT_CTOR(PostMessageEvent); |
michael@0 | 7748 | } |
michael@0 | 7749 | |
michael@0 | 7750 | ~PostMessageEvent() |
michael@0 | 7751 | { |
michael@0 | 7752 | MOZ_COUNT_DTOR(PostMessageEvent); |
michael@0 | 7753 | } |
michael@0 | 7754 | |
michael@0 | 7755 | JSAutoStructuredCloneBuffer& Buffer() |
michael@0 | 7756 | { |
michael@0 | 7757 | return mBuffer; |
michael@0 | 7758 | } |
michael@0 | 7759 | |
michael@0 | 7760 | bool StoreISupports(nsISupports* aSupports) |
michael@0 | 7761 | { |
michael@0 | 7762 | mSupportsArray.AppendElement(aSupports); |
michael@0 | 7763 | return true; |
michael@0 | 7764 | } |
michael@0 | 7765 | |
michael@0 | 7766 | private: |
michael@0 | 7767 | JSAutoStructuredCloneBuffer mBuffer; |
michael@0 | 7768 | nsRefPtr<nsGlobalWindow> mSource; |
michael@0 | 7769 | nsString mCallerOrigin; |
michael@0 | 7770 | nsRefPtr<nsGlobalWindow> mTargetWindow; |
michael@0 | 7771 | nsCOMPtr<nsIPrincipal> mProvidedPrincipal; |
michael@0 | 7772 | bool mTrustedCaller; |
michael@0 | 7773 | nsTArray<nsCOMPtr<nsISupports> > mSupportsArray; |
michael@0 | 7774 | }; |
michael@0 | 7775 | |
michael@0 | 7776 | namespace { |
michael@0 | 7777 | |
michael@0 | 7778 | struct StructuredCloneInfo { |
michael@0 | 7779 | PostMessageEvent* event; |
michael@0 | 7780 | bool subsumes; |
michael@0 | 7781 | nsPIDOMWindow* window; |
michael@0 | 7782 | nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> ports; |
michael@0 | 7783 | }; |
michael@0 | 7784 | |
michael@0 | 7785 | static JSObject* |
michael@0 | 7786 | PostMessageReadStructuredClone(JSContext* cx, |
michael@0 | 7787 | JSStructuredCloneReader* reader, |
michael@0 | 7788 | uint32_t tag, |
michael@0 | 7789 | uint32_t data, |
michael@0 | 7790 | void* closure) |
michael@0 | 7791 | { |
michael@0 | 7792 | if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) { |
michael@0 | 7793 | NS_ASSERTION(!data, "Data should be empty"); |
michael@0 | 7794 | |
michael@0 | 7795 | nsISupports* supports; |
michael@0 | 7796 | if (JS_ReadBytes(reader, &supports, sizeof(supports))) { |
michael@0 | 7797 | JS::Rooted<JS::Value> val(cx); |
michael@0 | 7798 | if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) { |
michael@0 | 7799 | return val.toObjectOrNull(); |
michael@0 | 7800 | } |
michael@0 | 7801 | } |
michael@0 | 7802 | } |
michael@0 | 7803 | |
michael@0 | 7804 | const JSStructuredCloneCallbacks* runtimeCallbacks = |
michael@0 | 7805 | js::GetContextStructuredCloneCallbacks(cx); |
michael@0 | 7806 | |
michael@0 | 7807 | if (runtimeCallbacks) { |
michael@0 | 7808 | return runtimeCallbacks->read(cx, reader, tag, data, nullptr); |
michael@0 | 7809 | } |
michael@0 | 7810 | |
michael@0 | 7811 | return nullptr; |
michael@0 | 7812 | } |
michael@0 | 7813 | |
michael@0 | 7814 | static bool |
michael@0 | 7815 | PostMessageWriteStructuredClone(JSContext* cx, |
michael@0 | 7816 | JSStructuredCloneWriter* writer, |
michael@0 | 7817 | JS::Handle<JSObject*> obj, |
michael@0 | 7818 | void *closure) |
michael@0 | 7819 | { |
michael@0 | 7820 | StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure); |
michael@0 | 7821 | NS_ASSERTION(scInfo, "Must have scInfo!"); |
michael@0 | 7822 | |
michael@0 | 7823 | nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative; |
michael@0 | 7824 | nsContentUtils::XPConnect()-> |
michael@0 | 7825 | GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative)); |
michael@0 | 7826 | if (wrappedNative) { |
michael@0 | 7827 | uint32_t scTag = 0; |
michael@0 | 7828 | nsISupports* supports = wrappedNative->Native(); |
michael@0 | 7829 | |
michael@0 | 7830 | nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports); |
michael@0 | 7831 | if (blob && scInfo->subsumes) |
michael@0 | 7832 | scTag = SCTAG_DOM_BLOB; |
michael@0 | 7833 | |
michael@0 | 7834 | nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports); |
michael@0 | 7835 | if (list && scInfo->subsumes) |
michael@0 | 7836 | scTag = SCTAG_DOM_FILELIST; |
michael@0 | 7837 | |
michael@0 | 7838 | if (scTag) |
michael@0 | 7839 | return JS_WriteUint32Pair(writer, scTag, 0) && |
michael@0 | 7840 | JS_WriteBytes(writer, &supports, sizeof(supports)) && |
michael@0 | 7841 | scInfo->event->StoreISupports(supports); |
michael@0 | 7842 | } |
michael@0 | 7843 | |
michael@0 | 7844 | const JSStructuredCloneCallbacks* runtimeCallbacks = |
michael@0 | 7845 | js::GetContextStructuredCloneCallbacks(cx); |
michael@0 | 7846 | |
michael@0 | 7847 | if (runtimeCallbacks) { |
michael@0 | 7848 | return runtimeCallbacks->write(cx, writer, obj, nullptr); |
michael@0 | 7849 | } |
michael@0 | 7850 | |
michael@0 | 7851 | return false; |
michael@0 | 7852 | } |
michael@0 | 7853 | |
michael@0 | 7854 | static bool |
michael@0 | 7855 | PostMessageReadTransferStructuredClone(JSContext* aCx, |
michael@0 | 7856 | JSStructuredCloneReader* reader, |
michael@0 | 7857 | uint32_t tag, void* aData, |
michael@0 | 7858 | uint64_t aExtraData, |
michael@0 | 7859 | void* aClosure, |
michael@0 | 7860 | JS::MutableHandle<JSObject*> returnObject) |
michael@0 | 7861 | { |
michael@0 | 7862 | StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure); |
michael@0 | 7863 | NS_ASSERTION(scInfo, "Must have scInfo!"); |
michael@0 | 7864 | |
michael@0 | 7865 | if (MessageChannel::PrefEnabled() && tag == SCTAG_DOM_MAP_MESSAGEPORT) { |
michael@0 | 7866 | MessagePort* port = static_cast<MessagePort*>(aData); |
michael@0 | 7867 | port->BindToOwner(scInfo->window); |
michael@0 | 7868 | scInfo->ports.Put(port, nullptr); |
michael@0 | 7869 | |
michael@0 | 7870 | JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx)); |
michael@0 | 7871 | if (JS_WrapObject(aCx, &obj)) { |
michael@0 | 7872 | MOZ_ASSERT(port->GetOwner() == scInfo->window); |
michael@0 | 7873 | returnObject.set(obj); |
michael@0 | 7874 | } |
michael@0 | 7875 | |
michael@0 | 7876 | return true; |
michael@0 | 7877 | } |
michael@0 | 7878 | |
michael@0 | 7879 | return false; |
michael@0 | 7880 | } |
michael@0 | 7881 | |
michael@0 | 7882 | static bool |
michael@0 | 7883 | PostMessageTransferStructuredClone(JSContext* aCx, |
michael@0 | 7884 | JS::Handle<JSObject*> aObj, |
michael@0 | 7885 | void* aClosure, |
michael@0 | 7886 | uint32_t* aTag, |
michael@0 | 7887 | JS::TransferableOwnership* aOwnership, |
michael@0 | 7888 | void** aContent, |
michael@0 | 7889 | uint64_t* aExtraData) |
michael@0 | 7890 | { |
michael@0 | 7891 | StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure); |
michael@0 | 7892 | NS_ASSERTION(scInfo, "Must have scInfo!"); |
michael@0 | 7893 | |
michael@0 | 7894 | if (MessageChannel::PrefEnabled()) { |
michael@0 | 7895 | MessagePortBase* port = nullptr; |
michael@0 | 7896 | nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port); |
michael@0 | 7897 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 7898 | nsRefPtr<MessagePortBase> newPort; |
michael@0 | 7899 | if (scInfo->ports.Get(port, getter_AddRefs(newPort))) { |
michael@0 | 7900 | // No duplicate. |
michael@0 | 7901 | return false; |
michael@0 | 7902 | } |
michael@0 | 7903 | |
michael@0 | 7904 | newPort = port->Clone(); |
michael@0 | 7905 | scInfo->ports.Put(port, newPort); |
michael@0 | 7906 | |
michael@0 | 7907 | *aTag = SCTAG_DOM_MAP_MESSAGEPORT; |
michael@0 | 7908 | *aOwnership = JS::SCTAG_TMO_CUSTOM; |
michael@0 | 7909 | *aContent = newPort; |
michael@0 | 7910 | *aExtraData = 0; |
michael@0 | 7911 | |
michael@0 | 7912 | return true; |
michael@0 | 7913 | } |
michael@0 | 7914 | } |
michael@0 | 7915 | |
michael@0 | 7916 | return false; |
michael@0 | 7917 | } |
michael@0 | 7918 | |
michael@0 | 7919 | void |
michael@0 | 7920 | PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership, |
michael@0 | 7921 | void *aContent, uint64_t aExtraData, void* aClosure) |
michael@0 | 7922 | { |
michael@0 | 7923 | StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure); |
michael@0 | 7924 | NS_ASSERTION(scInfo, "Must have scInfo!"); |
michael@0 | 7925 | |
michael@0 | 7926 | if (MessageChannel::PrefEnabled() && aTag == SCTAG_DOM_MAP_MESSAGEPORT) { |
michael@0 | 7927 | nsRefPtr<MessagePortBase> port(static_cast<MessagePort*>(aContent)); |
michael@0 | 7928 | scInfo->ports.Remove(port); |
michael@0 | 7929 | } |
michael@0 | 7930 | } |
michael@0 | 7931 | |
michael@0 | 7932 | JSStructuredCloneCallbacks kPostMessageCallbacks = { |
michael@0 | 7933 | PostMessageReadStructuredClone, |
michael@0 | 7934 | PostMessageWriteStructuredClone, |
michael@0 | 7935 | nullptr, |
michael@0 | 7936 | PostMessageReadTransferStructuredClone, |
michael@0 | 7937 | PostMessageTransferStructuredClone, |
michael@0 | 7938 | PostMessageFreeTransferStructuredClone |
michael@0 | 7939 | }; |
michael@0 | 7940 | |
michael@0 | 7941 | } // anonymous namespace |
michael@0 | 7942 | |
michael@0 | 7943 | static PLDHashOperator |
michael@0 | 7944 | PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure) |
michael@0 | 7945 | { |
michael@0 | 7946 | nsTArray<nsRefPtr<MessagePortBase> > *array = |
michael@0 | 7947 | static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure); |
michael@0 | 7948 | |
michael@0 | 7949 | array->AppendElement(aKey); |
michael@0 | 7950 | return PL_DHASH_NEXT; |
michael@0 | 7951 | } |
michael@0 | 7952 | |
michael@0 | 7953 | NS_IMETHODIMP |
michael@0 | 7954 | PostMessageEvent::Run() |
michael@0 | 7955 | { |
michael@0 | 7956 | NS_ABORT_IF_FALSE(mTargetWindow->IsOuterWindow(), |
michael@0 | 7957 | "should have been passed an outer window!"); |
michael@0 | 7958 | NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(), |
michael@0 | 7959 | "should have been passed an outer window!"); |
michael@0 | 7960 | |
michael@0 | 7961 | AutoJSAPI jsapi; |
michael@0 | 7962 | JSContext* cx = jsapi.cx(); |
michael@0 | 7963 | |
michael@0 | 7964 | // If we bailed before this point we're going to leak mMessage, but |
michael@0 | 7965 | // that's probably better than crashing. |
michael@0 | 7966 | |
michael@0 | 7967 | nsRefPtr<nsGlobalWindow> targetWindow; |
michael@0 | 7968 | if (mTargetWindow->IsClosedOrClosing() || |
michael@0 | 7969 | !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) || |
michael@0 | 7970 | targetWindow->IsClosedOrClosing()) |
michael@0 | 7971 | return NS_OK; |
michael@0 | 7972 | |
michael@0 | 7973 | NS_ABORT_IF_FALSE(targetWindow->IsInnerWindow(), |
michael@0 | 7974 | "we ordered an inner window!"); |
michael@0 | 7975 | JSAutoCompartment ac(cx, targetWindow->GetWrapperPreserveColor()); |
michael@0 | 7976 | |
michael@0 | 7977 | // Ensure that any origin which might have been provided is the origin of this |
michael@0 | 7978 | // window's document. Note that we do this *now* instead of when postMessage |
michael@0 | 7979 | // is called because the target window might have been navigated to a |
michael@0 | 7980 | // different location between then and now. If this check happened when |
michael@0 | 7981 | // postMessage was called, it would be fairly easy for a malicious webpage to |
michael@0 | 7982 | // intercept messages intended for another site by carefully timing navigation |
michael@0 | 7983 | // of the target window so it changed location after postMessage but before |
michael@0 | 7984 | // now. |
michael@0 | 7985 | if (mProvidedPrincipal) { |
michael@0 | 7986 | // Get the target's origin either from its principal or, in the case the |
michael@0 | 7987 | // principal doesn't carry a URI (e.g. the system principal), the target's |
michael@0 | 7988 | // document. |
michael@0 | 7989 | nsIPrincipal* targetPrin = targetWindow->GetPrincipal(); |
michael@0 | 7990 | if (NS_WARN_IF(!targetPrin)) |
michael@0 | 7991 | return NS_OK; |
michael@0 | 7992 | |
michael@0 | 7993 | // Note: This is contrary to the spec with respect to file: URLs, which |
michael@0 | 7994 | // the spec groups into a single origin, but given we intentionally |
michael@0 | 7995 | // don't do that in other places it seems better to hold the line for |
michael@0 | 7996 | // now. Long-term, we want HTML5 to address this so that we can |
michael@0 | 7997 | // be compliant while being safer. |
michael@0 | 7998 | if (!targetPrin->Equals(mProvidedPrincipal)) { |
michael@0 | 7999 | return NS_OK; |
michael@0 | 8000 | } |
michael@0 | 8001 | } |
michael@0 | 8002 | |
michael@0 | 8003 | // Deserialize the structured clone data |
michael@0 | 8004 | JS::Rooted<JS::Value> messageData(cx); |
michael@0 | 8005 | StructuredCloneInfo scInfo; |
michael@0 | 8006 | scInfo.event = this; |
michael@0 | 8007 | scInfo.window = targetWindow; |
michael@0 | 8008 | |
michael@0 | 8009 | if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) { |
michael@0 | 8010 | return NS_ERROR_DOM_DATA_CLONE_ERR; |
michael@0 | 8011 | } |
michael@0 | 8012 | |
michael@0 | 8013 | // Create the event |
michael@0 | 8014 | nsCOMPtr<mozilla::dom::EventTarget> eventTarget = |
michael@0 | 8015 | do_QueryInterface(static_cast<nsPIDOMWindow*>(targetWindow.get())); |
michael@0 | 8016 | nsRefPtr<MessageEvent> event = |
michael@0 | 8017 | new MessageEvent(eventTarget, nullptr, nullptr); |
michael@0 | 8018 | |
michael@0 | 8019 | event->InitMessageEvent(NS_LITERAL_STRING("message"), false /*non-bubbling */, |
michael@0 | 8020 | false /*cancelable */, messageData, mCallerOrigin, |
michael@0 | 8021 | EmptyString(), mSource); |
michael@0 | 8022 | |
michael@0 | 8023 | nsTArray<nsRefPtr<MessagePortBase> > ports; |
michael@0 | 8024 | scInfo.ports.EnumerateRead(PopulateMessagePortList, &ports); |
michael@0 | 8025 | event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports)); |
michael@0 | 8026 | |
michael@0 | 8027 | // We can't simply call dispatchEvent on the window because doing so ends |
michael@0 | 8028 | // up flipping the trusted bit on the event, and we don't want that to |
michael@0 | 8029 | // happen because then untrusted content can call postMessage on a chrome |
michael@0 | 8030 | // window if it can get a reference to it. |
michael@0 | 8031 | |
michael@0 | 8032 | nsIPresShell *shell = targetWindow->mDoc->GetShell(); |
michael@0 | 8033 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 8034 | if (shell) |
michael@0 | 8035 | presContext = shell->GetPresContext(); |
michael@0 | 8036 | |
michael@0 | 8037 | event->SetTrusted(mTrustedCaller); |
michael@0 | 8038 | WidgetEvent* internalEvent = event->GetInternalNSEvent(); |
michael@0 | 8039 | |
michael@0 | 8040 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 8041 | EventDispatcher::Dispatch(static_cast<nsPIDOMWindow*>(mTargetWindow), |
michael@0 | 8042 | presContext, |
michael@0 | 8043 | internalEvent, |
michael@0 | 8044 | static_cast<dom::Event*>(event.get()), |
michael@0 | 8045 | &status); |
michael@0 | 8046 | return NS_OK; |
michael@0 | 8047 | } |
michael@0 | 8048 | |
michael@0 | 8049 | void |
michael@0 | 8050 | nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage, |
michael@0 | 8051 | const nsAString& aTargetOrigin, |
michael@0 | 8052 | JS::Handle<JS::Value> aTransfer, |
michael@0 | 8053 | ErrorResult& aError) |
michael@0 | 8054 | { |
michael@0 | 8055 | FORWARD_TO_OUTER_OR_THROW(PostMessageMoz, |
michael@0 | 8056 | (aCx, aMessage, aTargetOrigin, aTransfer, aError), |
michael@0 | 8057 | aError, ); |
michael@0 | 8058 | |
michael@0 | 8059 | // |
michael@0 | 8060 | // Window.postMessage is an intentional subversion of the same-origin policy. |
michael@0 | 8061 | // As such, this code must be particularly careful in the information it |
michael@0 | 8062 | // exposes to calling code. |
michael@0 | 8063 | // |
michael@0 | 8064 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html |
michael@0 | 8065 | // |
michael@0 | 8066 | |
michael@0 | 8067 | // First, get the caller's window |
michael@0 | 8068 | nsRefPtr<nsGlobalWindow> callerInnerWin = CallerInnerWindow(); |
michael@0 | 8069 | nsIPrincipal* callerPrin; |
michael@0 | 8070 | if (callerInnerWin) { |
michael@0 | 8071 | NS_ABORT_IF_FALSE(callerInnerWin->IsInnerWindow(), |
michael@0 | 8072 | "should have gotten an inner window here"); |
michael@0 | 8073 | |
michael@0 | 8074 | // Compute the caller's origin either from its principal or, in the case the |
michael@0 | 8075 | // principal doesn't carry a URI (e.g. the system principal), the caller's |
michael@0 | 8076 | // document. We must get this now instead of when the event is created and |
michael@0 | 8077 | // dispatched, because ultimately it is the identity of the calling window |
michael@0 | 8078 | // *now* that determines who sent the message (and not an identity which might |
michael@0 | 8079 | // have changed due to intervening navigations). |
michael@0 | 8080 | callerPrin = callerInnerWin->GetPrincipal(); |
michael@0 | 8081 | } |
michael@0 | 8082 | else { |
michael@0 | 8083 | // In case the global is not a window, it can be a sandbox, and the sandbox's |
michael@0 | 8084 | // principal can be used for the security check. |
michael@0 | 8085 | JSObject *global = CallerGlobal(); |
michael@0 | 8086 | NS_ASSERTION(global, "Why is there no global object?"); |
michael@0 | 8087 | JSCompartment *compartment = js::GetObjectCompartment(global); |
michael@0 | 8088 | callerPrin = xpc::GetCompartmentPrincipal(compartment); |
michael@0 | 8089 | } |
michael@0 | 8090 | if (!callerPrin) { |
michael@0 | 8091 | return; |
michael@0 | 8092 | } |
michael@0 | 8093 | |
michael@0 | 8094 | nsCOMPtr<nsIURI> callerOuterURI; |
michael@0 | 8095 | if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) { |
michael@0 | 8096 | return; |
michael@0 | 8097 | } |
michael@0 | 8098 | |
michael@0 | 8099 | nsAutoString origin; |
michael@0 | 8100 | if (callerOuterURI) { |
michael@0 | 8101 | // if the principal has a URI, use that to generate the origin |
michael@0 | 8102 | nsContentUtils::GetUTFOrigin(callerPrin, origin); |
michael@0 | 8103 | } |
michael@0 | 8104 | else if (callerInnerWin) { |
michael@0 | 8105 | // otherwise use the URI of the document to generate origin |
michael@0 | 8106 | nsCOMPtr<nsIDocument> doc = callerInnerWin->GetExtantDoc(); |
michael@0 | 8107 | if (!doc) { |
michael@0 | 8108 | return; |
michael@0 | 8109 | } |
michael@0 | 8110 | callerOuterURI = doc->GetDocumentURI(); |
michael@0 | 8111 | // if the principal has a URI, use that to generate the origin |
michael@0 | 8112 | nsContentUtils::GetUTFOrigin(callerOuterURI, origin); |
michael@0 | 8113 | } |
michael@0 | 8114 | else { |
michael@0 | 8115 | // in case of a sandbox with a system principal origin can be empty |
michael@0 | 8116 | if (!nsContentUtils::IsSystemPrincipal(callerPrin)) { |
michael@0 | 8117 | return; |
michael@0 | 8118 | } |
michael@0 | 8119 | } |
michael@0 | 8120 | |
michael@0 | 8121 | // Convert the provided origin string into a URI for comparison purposes. |
michael@0 | 8122 | nsCOMPtr<nsIPrincipal> providedPrincipal; |
michael@0 | 8123 | |
michael@0 | 8124 | if (aTargetOrigin.EqualsASCII("/")) { |
michael@0 | 8125 | providedPrincipal = BrokenGetEntryGlobal()->PrincipalOrNull(); |
michael@0 | 8126 | if (NS_WARN_IF(!providedPrincipal)) |
michael@0 | 8127 | return; |
michael@0 | 8128 | } |
michael@0 | 8129 | |
michael@0 | 8130 | // "*" indicates no specific origin is required. |
michael@0 | 8131 | else if (!aTargetOrigin.EqualsASCII("*")) { |
michael@0 | 8132 | nsCOMPtr<nsIURI> originURI; |
michael@0 | 8133 | if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) { |
michael@0 | 8134 | aError.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
michael@0 | 8135 | return; |
michael@0 | 8136 | } |
michael@0 | 8137 | |
michael@0 | 8138 | if (NS_FAILED(originURI->SetUserPass(EmptyCString())) || |
michael@0 | 8139 | NS_FAILED(originURI->SetPath(EmptyCString()))) { |
michael@0 | 8140 | return; |
michael@0 | 8141 | } |
michael@0 | 8142 | |
michael@0 | 8143 | nsCOMPtr<nsIScriptSecurityManager> ssm = |
michael@0 | 8144 | nsContentUtils::GetSecurityManager(); |
michael@0 | 8145 | MOZ_ASSERT(ssm); |
michael@0 | 8146 | |
michael@0 | 8147 | nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSubjectPrincipal(); |
michael@0 | 8148 | MOZ_ASSERT(principal); |
michael@0 | 8149 | |
michael@0 | 8150 | uint32_t appId; |
michael@0 | 8151 | if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId)))) |
michael@0 | 8152 | return; |
michael@0 | 8153 | |
michael@0 | 8154 | bool isInBrowser; |
michael@0 | 8155 | if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser)))) |
michael@0 | 8156 | return; |
michael@0 | 8157 | |
michael@0 | 8158 | // Create a nsIPrincipal inheriting the app/browser attributes from the |
michael@0 | 8159 | // caller. |
michael@0 | 8160 | nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser, |
michael@0 | 8161 | getter_AddRefs(providedPrincipal)); |
michael@0 | 8162 | if (NS_WARN_IF(NS_FAILED(rv))) { |
michael@0 | 8163 | return; |
michael@0 | 8164 | } |
michael@0 | 8165 | } |
michael@0 | 8166 | |
michael@0 | 8167 | // Create and asynchronously dispatch a runnable which will handle actual DOM |
michael@0 | 8168 | // event creation and dispatch. |
michael@0 | 8169 | nsRefPtr<PostMessageEvent> event = |
michael@0 | 8170 | new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin |
michael@0 | 8171 | ? nullptr |
michael@0 | 8172 | : callerInnerWin->GetOuterWindowInternal(), |
michael@0 | 8173 | origin, |
michael@0 | 8174 | this, |
michael@0 | 8175 | providedPrincipal, |
michael@0 | 8176 | nsContentUtils::IsCallerChrome()); |
michael@0 | 8177 | |
michael@0 | 8178 | // We *must* clone the data here, or the JS::Value could be modified |
michael@0 | 8179 | // by script |
michael@0 | 8180 | StructuredCloneInfo scInfo; |
michael@0 | 8181 | scInfo.event = event; |
michael@0 | 8182 | scInfo.window = this; |
michael@0 | 8183 | |
michael@0 | 8184 | nsIPrincipal* principal = GetPrincipal(); |
michael@0 | 8185 | JS::Rooted<JS::Value> message(aCx, aMessage); |
michael@0 | 8186 | JS::Rooted<JS::Value> transfer(aCx, aTransfer); |
michael@0 | 8187 | if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)) || |
michael@0 | 8188 | !event->Buffer().write(aCx, message, transfer, &kPostMessageCallbacks, |
michael@0 | 8189 | &scInfo)) { |
michael@0 | 8190 | aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); |
michael@0 | 8191 | return; |
michael@0 | 8192 | } |
michael@0 | 8193 | |
michael@0 | 8194 | aError = NS_DispatchToCurrentThread(event); |
michael@0 | 8195 | } |
michael@0 | 8196 | |
michael@0 | 8197 | void |
michael@0 | 8198 | nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage, |
michael@0 | 8199 | const nsAString& aTargetOrigin, |
michael@0 | 8200 | const Optional<Sequence<JS::Value > >& aTransfer, |
michael@0 | 8201 | ErrorResult& aError) |
michael@0 | 8202 | { |
michael@0 | 8203 | JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue()); |
michael@0 | 8204 | if (aTransfer.WasPassed()) { |
michael@0 | 8205 | const Sequence<JS::Value >& values = aTransfer.Value(); |
michael@0 | 8206 | |
michael@0 | 8207 | // The input sequence only comes from the generated bindings code, which |
michael@0 | 8208 | // ensures it is rooted. |
michael@0 | 8209 | JS::HandleValueArray elements = |
michael@0 | 8210 | JS::HandleValueArray::fromMarkedLocation(values.Length(), values.Elements()); |
michael@0 | 8211 | |
michael@0 | 8212 | transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements)); |
michael@0 | 8213 | if (transferArray.isNull()) { |
michael@0 | 8214 | aError.Throw(NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 8215 | return; |
michael@0 | 8216 | } |
michael@0 | 8217 | } |
michael@0 | 8218 | |
michael@0 | 8219 | PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray, aError); |
michael@0 | 8220 | } |
michael@0 | 8221 | |
michael@0 | 8222 | NS_IMETHODIMP |
michael@0 | 8223 | nsGlobalWindow::PostMessageMoz(JS::Handle<JS::Value> aMessage, |
michael@0 | 8224 | const nsAString& aOrigin, |
michael@0 | 8225 | JS::Handle<JS::Value> aTransfer, |
michael@0 | 8226 | JSContext* aCx) |
michael@0 | 8227 | { |
michael@0 | 8228 | ErrorResult rv; |
michael@0 | 8229 | PostMessageMoz(aCx, aMessage, aOrigin, aTransfer, rv); |
michael@0 | 8230 | |
michael@0 | 8231 | return rv.ErrorCode(); |
michael@0 | 8232 | } |
michael@0 | 8233 | |
michael@0 | 8234 | class nsCloseEvent : public nsRunnable { |
michael@0 | 8235 | |
michael@0 | 8236 | nsRefPtr<nsGlobalWindow> mWindow; |
michael@0 | 8237 | bool mIndirect; |
michael@0 | 8238 | |
michael@0 | 8239 | nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect) |
michael@0 | 8240 | : mWindow(aWindow) |
michael@0 | 8241 | , mIndirect(aIndirect) |
michael@0 | 8242 | {} |
michael@0 | 8243 | |
michael@0 | 8244 | public: |
michael@0 | 8245 | |
michael@0 | 8246 | static nsresult |
michael@0 | 8247 | PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) { |
michael@0 | 8248 | nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect); |
michael@0 | 8249 | nsresult rv = NS_DispatchToCurrentThread(ev); |
michael@0 | 8250 | if (NS_SUCCEEDED(rv)) |
michael@0 | 8251 | aWindow->MaybeForgiveSpamCount(); |
michael@0 | 8252 | return rv; |
michael@0 | 8253 | } |
michael@0 | 8254 | |
michael@0 | 8255 | NS_IMETHOD Run() { |
michael@0 | 8256 | if (mWindow) { |
michael@0 | 8257 | if (mIndirect) { |
michael@0 | 8258 | return PostCloseEvent(mWindow, false); |
michael@0 | 8259 | } |
michael@0 | 8260 | mWindow->ReallyCloseWindow(); |
michael@0 | 8261 | } |
michael@0 | 8262 | return NS_OK; |
michael@0 | 8263 | } |
michael@0 | 8264 | |
michael@0 | 8265 | }; |
michael@0 | 8266 | |
michael@0 | 8267 | bool |
michael@0 | 8268 | nsGlobalWindow::CanClose() |
michael@0 | 8269 | { |
michael@0 | 8270 | if (!mDocShell) |
michael@0 | 8271 | return true; |
michael@0 | 8272 | |
michael@0 | 8273 | // Ask the content viewer whether the toplevel window can close. |
michael@0 | 8274 | // If the content viewer returns false, it is responsible for calling |
michael@0 | 8275 | // Close() as soon as it is possible for the window to close. |
michael@0 | 8276 | // This allows us to not close the window while printing is happening. |
michael@0 | 8277 | |
michael@0 | 8278 | nsCOMPtr<nsIContentViewer> cv; |
michael@0 | 8279 | mDocShell->GetContentViewer(getter_AddRefs(cv)); |
michael@0 | 8280 | if (cv) { |
michael@0 | 8281 | bool canClose; |
michael@0 | 8282 | nsresult rv = cv->PermitUnload(false, &canClose); |
michael@0 | 8283 | if (NS_SUCCEEDED(rv) && !canClose) |
michael@0 | 8284 | return false; |
michael@0 | 8285 | |
michael@0 | 8286 | rv = cv->RequestWindowClose(&canClose); |
michael@0 | 8287 | if (NS_SUCCEEDED(rv) && !canClose) |
michael@0 | 8288 | return false; |
michael@0 | 8289 | } |
michael@0 | 8290 | |
michael@0 | 8291 | return true; |
michael@0 | 8292 | } |
michael@0 | 8293 | |
michael@0 | 8294 | void |
michael@0 | 8295 | nsGlobalWindow::Close(ErrorResult& aError) |
michael@0 | 8296 | { |
michael@0 | 8297 | FORWARD_TO_OUTER_OR_THROW(Close, (aError), aError, ); |
michael@0 | 8298 | |
michael@0 | 8299 | if (!mDocShell || IsInModalState() || |
michael@0 | 8300 | (IsFrame() && !mDocShell->GetIsBrowserOrApp())) { |
michael@0 | 8301 | // window.close() is called on a frame in a frameset, on a window |
michael@0 | 8302 | // that's already closed, or on a window for which there's |
michael@0 | 8303 | // currently a modal dialog open. Ignore such calls. |
michael@0 | 8304 | return; |
michael@0 | 8305 | } |
michael@0 | 8306 | |
michael@0 | 8307 | if (mHavePendingClose) { |
michael@0 | 8308 | // We're going to be closed anyway; do nothing since we don't want |
michael@0 | 8309 | // to double-close |
michael@0 | 8310 | return; |
michael@0 | 8311 | } |
michael@0 | 8312 | |
michael@0 | 8313 | if (mBlockScriptedClosingFlag) |
michael@0 | 8314 | { |
michael@0 | 8315 | // A script's popup has been blocked and we don't want |
michael@0 | 8316 | // the window to be closed directly after this event, |
michael@0 | 8317 | // so the user can see that there was a blocked popup. |
michael@0 | 8318 | return; |
michael@0 | 8319 | } |
michael@0 | 8320 | |
michael@0 | 8321 | // Don't allow scripts from content to close non-app or non-neterror |
michael@0 | 8322 | // windows that were not opened by script. |
michael@0 | 8323 | nsAutoString url; |
michael@0 | 8324 | mDoc->GetURL(url); |
michael@0 | 8325 | if (!mDocShell->GetIsApp() && |
michael@0 | 8326 | !StringBeginsWith(url, NS_LITERAL_STRING("about:neterror")) && |
michael@0 | 8327 | !mHadOriginalOpener && !nsContentUtils::IsCallerChrome()) { |
michael@0 | 8328 | bool allowClose = mAllowScriptsToClose || |
michael@0 | 8329 | Preferences::GetBool("dom.allow_scripts_to_close_windows", true); |
michael@0 | 8330 | if (!allowClose) { |
michael@0 | 8331 | // We're blocking the close operation |
michael@0 | 8332 | // report localized error msg in JS console |
michael@0 | 8333 | nsContentUtils::ReportToConsole( |
michael@0 | 8334 | nsIScriptError::warningFlag, |
michael@0 | 8335 | NS_LITERAL_CSTRING("DOM Window"), mDoc, // Better name for the category? |
michael@0 | 8336 | nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 8337 | "WindowCloseBlockedWarning"); |
michael@0 | 8338 | |
michael@0 | 8339 | return; |
michael@0 | 8340 | } |
michael@0 | 8341 | } |
michael@0 | 8342 | |
michael@0 | 8343 | if (!mInClose && !mIsClosed && !CanClose()) { |
michael@0 | 8344 | return; |
michael@0 | 8345 | } |
michael@0 | 8346 | |
michael@0 | 8347 | // Fire a DOM event notifying listeners that this window is about to |
michael@0 | 8348 | // be closed. The tab UI code may choose to cancel the default |
michael@0 | 8349 | // action for this event, if so, we won't actually close the window |
michael@0 | 8350 | // (since the tab UI code will close the tab in stead). Sure, this |
michael@0 | 8351 | // could be abused by content code, but do we care? I don't think |
michael@0 | 8352 | // so... |
michael@0 | 8353 | |
michael@0 | 8354 | bool wasInClose = mInClose; |
michael@0 | 8355 | mInClose = true; |
michael@0 | 8356 | |
michael@0 | 8357 | if (!DispatchCustomEvent("DOMWindowClose")) { |
michael@0 | 8358 | // Someone chose to prevent the default action for this event, if |
michael@0 | 8359 | // so, let's not close this window after all... |
michael@0 | 8360 | |
michael@0 | 8361 | mInClose = wasInClose; |
michael@0 | 8362 | return; |
michael@0 | 8363 | } |
michael@0 | 8364 | |
michael@0 | 8365 | aError = FinalClose(); |
michael@0 | 8366 | } |
michael@0 | 8367 | |
michael@0 | 8368 | NS_IMETHODIMP |
michael@0 | 8369 | nsGlobalWindow::Close() |
michael@0 | 8370 | { |
michael@0 | 8371 | ErrorResult rv; |
michael@0 | 8372 | Close(rv); |
michael@0 | 8373 | |
michael@0 | 8374 | return rv.ErrorCode(); |
michael@0 | 8375 | } |
michael@0 | 8376 | |
michael@0 | 8377 | nsresult |
michael@0 | 8378 | nsGlobalWindow::ForceClose() |
michael@0 | 8379 | { |
michael@0 | 8380 | if (IsFrame() || !mDocShell) { |
michael@0 | 8381 | // This may be a frame in a frameset, or a window that's already closed. |
michael@0 | 8382 | // Ignore such calls. |
michael@0 | 8383 | |
michael@0 | 8384 | return NS_OK; |
michael@0 | 8385 | } |
michael@0 | 8386 | |
michael@0 | 8387 | if (mHavePendingClose) { |
michael@0 | 8388 | // We're going to be closed anyway; do nothing since we don't want |
michael@0 | 8389 | // to double-close |
michael@0 | 8390 | return NS_OK; |
michael@0 | 8391 | } |
michael@0 | 8392 | |
michael@0 | 8393 | mInClose = true; |
michael@0 | 8394 | |
michael@0 | 8395 | DispatchCustomEvent("DOMWindowClose"); |
michael@0 | 8396 | |
michael@0 | 8397 | return FinalClose(); |
michael@0 | 8398 | } |
michael@0 | 8399 | |
michael@0 | 8400 | nsresult |
michael@0 | 8401 | nsGlobalWindow::FinalClose() |
michael@0 | 8402 | { |
michael@0 | 8403 | // Flag that we were closed. |
michael@0 | 8404 | mIsClosed = true; |
michael@0 | 8405 | |
michael@0 | 8406 | // This stuff is non-sensical but incredibly fragile. The reasons for the |
michael@0 | 8407 | // behavior here don't make sense today and may not have ever made sense, |
michael@0 | 8408 | // but various bits of frontend code break when you change them. If you need |
michael@0 | 8409 | // to fix up this behavior, feel free to. It's a righteous task, but involves |
michael@0 | 8410 | // wrestling with various download manager tests, frontend code, and possible |
michael@0 | 8411 | // broken addons. The chrome tests in toolkit/mozapps/downloads are a good |
michael@0 | 8412 | // testing ground. |
michael@0 | 8413 | // |
michael@0 | 8414 | // In particular, if |win|'s JSContext is at the top of the stack, we must |
michael@0 | 8415 | // complete _two_ round-trips to the event loop before the call to |
michael@0 | 8416 | // ReallyCloseWindow. This allows setTimeout handlers that are set after |
michael@0 | 8417 | // FinalClose() is called to run before the window is torn down. |
michael@0 | 8418 | bool indirect = GetContextInternal() && // Occasionally null. See bug 877390. |
michael@0 | 8419 | (nsContentUtils::GetCurrentJSContext() == |
michael@0 | 8420 | GetContextInternal()->GetNativeContext()); |
michael@0 | 8421 | if (NS_FAILED(nsCloseEvent::PostCloseEvent(this, indirect))) { |
michael@0 | 8422 | ReallyCloseWindow(); |
michael@0 | 8423 | } else { |
michael@0 | 8424 | mHavePendingClose = true; |
michael@0 | 8425 | } |
michael@0 | 8426 | |
michael@0 | 8427 | return NS_OK; |
michael@0 | 8428 | } |
michael@0 | 8429 | |
michael@0 | 8430 | |
michael@0 | 8431 | void |
michael@0 | 8432 | nsGlobalWindow::ReallyCloseWindow() |
michael@0 | 8433 | { |
michael@0 | 8434 | FORWARD_TO_OUTER_VOID(ReallyCloseWindow, ()); |
michael@0 | 8435 | |
michael@0 | 8436 | // Make sure we never reenter this method. |
michael@0 | 8437 | mHavePendingClose = true; |
michael@0 | 8438 | |
michael@0 | 8439 | nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow(); |
michael@0 | 8440 | |
michael@0 | 8441 | // If there's no treeOwnerAsWin, this window must already be closed. |
michael@0 | 8442 | |
michael@0 | 8443 | if (treeOwnerAsWin) { |
michael@0 | 8444 | |
michael@0 | 8445 | // but if we're a browser window we could be in some nasty |
michael@0 | 8446 | // self-destroying cascade that we should mostly ignore |
michael@0 | 8447 | |
michael@0 | 8448 | if (mDocShell) { |
michael@0 | 8449 | nsCOMPtr<nsIBrowserDOMWindow> bwin; |
michael@0 | 8450 | nsCOMPtr<nsIDocShellTreeItem> rootItem; |
michael@0 | 8451 | mDocShell->GetRootTreeItem(getter_AddRefs(rootItem)); |
michael@0 | 8452 | nsCOMPtr<nsIDOMWindow> rootWin(do_GetInterface(rootItem)); |
michael@0 | 8453 | nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin)); |
michael@0 | 8454 | if (chromeWin) |
michael@0 | 8455 | chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin)); |
michael@0 | 8456 | |
michael@0 | 8457 | if (rootWin) { |
michael@0 | 8458 | /* Normally we destroy the entire window, but not if |
michael@0 | 8459 | this DOM window belongs to a tabbed browser and doesn't |
michael@0 | 8460 | correspond to a tab. This allows a well-behaved tab |
michael@0 | 8461 | to destroy the container as it should but is a final measure |
michael@0 | 8462 | to prevent an errant tab from doing so when it shouldn't. |
michael@0 | 8463 | This works because we reach this code when we shouldn't only |
michael@0 | 8464 | in the particular circumstance that we belong to a tab |
michael@0 | 8465 | that has just been closed (and is therefore already missing |
michael@0 | 8466 | from the list of browsers) (and has an unload handler |
michael@0 | 8467 | that closes the window). */ |
michael@0 | 8468 | // XXXbz now that we have mHavePendingClose, is this needed? |
michael@0 | 8469 | bool isTab = false; |
michael@0 | 8470 | if (rootWin == this || |
michael@0 | 8471 | !bwin || (bwin->IsTabContentWindow(GetOuterWindowInternal(), |
michael@0 | 8472 | &isTab), isTab)) |
michael@0 | 8473 | treeOwnerAsWin->Destroy(); |
michael@0 | 8474 | } |
michael@0 | 8475 | } |
michael@0 | 8476 | |
michael@0 | 8477 | CleanUp(); |
michael@0 | 8478 | } |
michael@0 | 8479 | } |
michael@0 | 8480 | |
michael@0 | 8481 | void |
michael@0 | 8482 | nsGlobalWindow::EnterModalState() |
michael@0 | 8483 | { |
michael@0 | 8484 | MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows"); |
michael@0 | 8485 | |
michael@0 | 8486 | // GetScriptableTop, not GetTop, so that EnterModalState works properly with |
michael@0 | 8487 | // <iframe mozbrowser>. |
michael@0 | 8488 | nsGlobalWindow* topWin = GetScriptableTop(); |
michael@0 | 8489 | |
michael@0 | 8490 | if (!topWin) { |
michael@0 | 8491 | NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?"); |
michael@0 | 8492 | return; |
michael@0 | 8493 | } |
michael@0 | 8494 | |
michael@0 | 8495 | // If there is an active ESM in this window, clear it. Otherwise, this can |
michael@0 | 8496 | // cause a problem if a modal state is entered during a mouseup event. |
michael@0 | 8497 | EventStateManager* activeESM = |
michael@0 | 8498 | static_cast<EventStateManager*>( |
michael@0 | 8499 | EventStateManager::GetActiveEventStateManager()); |
michael@0 | 8500 | if (activeESM && activeESM->GetPresContext()) { |
michael@0 | 8501 | nsIPresShell* activeShell = activeESM->GetPresContext()->GetPresShell(); |
michael@0 | 8502 | if (activeShell && ( |
michael@0 | 8503 | nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(), mDoc) || |
michael@0 | 8504 | nsContentUtils::ContentIsCrossDocDescendantOf(mDoc, activeShell->GetDocument()))) { |
michael@0 | 8505 | EventStateManager::ClearGlobalActiveContent(activeESM); |
michael@0 | 8506 | |
michael@0 | 8507 | activeShell->SetCapturingContent(nullptr, 0); |
michael@0 | 8508 | |
michael@0 | 8509 | if (activeShell) { |
michael@0 | 8510 | nsRefPtr<nsFrameSelection> frameSelection = activeShell->FrameSelection(); |
michael@0 | 8511 | frameSelection->SetMouseDownState(false); |
michael@0 | 8512 | } |
michael@0 | 8513 | } |
michael@0 | 8514 | } |
michael@0 | 8515 | |
michael@0 | 8516 | // If there are any drag and drop operations in flight, try to end them. |
michael@0 | 8517 | nsCOMPtr<nsIDragService> ds = |
michael@0 | 8518 | do_GetService("@mozilla.org/widget/dragservice;1"); |
michael@0 | 8519 | if (ds) { |
michael@0 | 8520 | ds->EndDragSession(true); |
michael@0 | 8521 | } |
michael@0 | 8522 | |
michael@0 | 8523 | // Clear the capturing content if it is under topDoc. |
michael@0 | 8524 | // Usually the activeESM check above does that, but there are cases when |
michael@0 | 8525 | // we don't have activeESM, or it is for different document. |
michael@0 | 8526 | nsIDocument* topDoc = topWin->GetExtantDoc(); |
michael@0 | 8527 | nsIContent* capturingContent = nsIPresShell::GetCapturingContent(); |
michael@0 | 8528 | if (capturingContent && topDoc && |
michael@0 | 8529 | nsContentUtils::ContentIsCrossDocDescendantOf(capturingContent, topDoc)) { |
michael@0 | 8530 | nsIPresShell::SetCapturingContent(nullptr, 0); |
michael@0 | 8531 | } |
michael@0 | 8532 | |
michael@0 | 8533 | if (topWin->mModalStateDepth == 0) { |
michael@0 | 8534 | NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!"); |
michael@0 | 8535 | |
michael@0 | 8536 | mSuspendedDoc = topDoc; |
michael@0 | 8537 | if (mSuspendedDoc) { |
michael@0 | 8538 | mSuspendedDoc->SuppressEventHandling(nsIDocument::eAnimationsOnly); |
michael@0 | 8539 | } |
michael@0 | 8540 | } |
michael@0 | 8541 | topWin->mModalStateDepth++; |
michael@0 | 8542 | } |
michael@0 | 8543 | |
michael@0 | 8544 | // static |
michael@0 | 8545 | void |
michael@0 | 8546 | nsGlobalWindow::RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow, |
michael@0 | 8547 | nsGlobalWindow *aWindow) |
michael@0 | 8548 | { |
michael@0 | 8549 | nsGlobalWindow *inner; |
michael@0 | 8550 | |
michael@0 | 8551 | // Return early if we're frozen or have no inner window. |
michael@0 | 8552 | if (!(inner = aWindow->GetCurrentInnerWindowInternal()) || |
michael@0 | 8553 | inner->IsFrozen()) { |
michael@0 | 8554 | return; |
michael@0 | 8555 | } |
michael@0 | 8556 | |
michael@0 | 8557 | inner->RunTimeout(nullptr); |
michael@0 | 8558 | |
michael@0 | 8559 | // Check again if we're frozen since running pending timeouts |
michael@0 | 8560 | // could've frozen us. |
michael@0 | 8561 | if (inner->IsFrozen()) { |
michael@0 | 8562 | return; |
michael@0 | 8563 | } |
michael@0 | 8564 | |
michael@0 | 8565 | nsCOMPtr<nsIDOMWindowCollection> frames; |
michael@0 | 8566 | aWindow->GetFrames(getter_AddRefs(frames)); |
michael@0 | 8567 | |
michael@0 | 8568 | if (!frames) { |
michael@0 | 8569 | return; |
michael@0 | 8570 | } |
michael@0 | 8571 | |
michael@0 | 8572 | uint32_t i, length; |
michael@0 | 8573 | if (NS_FAILED(frames->GetLength(&length)) || !length) { |
michael@0 | 8574 | return; |
michael@0 | 8575 | } |
michael@0 | 8576 | |
michael@0 | 8577 | for (i = 0; i < length && aTopWindow->mModalStateDepth == 0; i++) { |
michael@0 | 8578 | nsCOMPtr<nsIDOMWindow> child; |
michael@0 | 8579 | frames->Item(i, getter_AddRefs(child)); |
michael@0 | 8580 | |
michael@0 | 8581 | if (!child) { |
michael@0 | 8582 | return; |
michael@0 | 8583 | } |
michael@0 | 8584 | |
michael@0 | 8585 | nsGlobalWindow *childWin = |
michael@0 | 8586 | static_cast<nsGlobalWindow *> |
michael@0 | 8587 | (static_cast<nsIDOMWindow *> |
michael@0 | 8588 | (child.get())); |
michael@0 | 8589 | |
michael@0 | 8590 | RunPendingTimeoutsRecursive(aTopWindow, childWin); |
michael@0 | 8591 | } |
michael@0 | 8592 | } |
michael@0 | 8593 | |
michael@0 | 8594 | class nsPendingTimeoutRunner : public nsRunnable |
michael@0 | 8595 | { |
michael@0 | 8596 | public: |
michael@0 | 8597 | nsPendingTimeoutRunner(nsGlobalWindow *aWindow) |
michael@0 | 8598 | : mWindow(aWindow) |
michael@0 | 8599 | { |
michael@0 | 8600 | NS_ASSERTION(mWindow, "mWindow is null."); |
michael@0 | 8601 | } |
michael@0 | 8602 | |
michael@0 | 8603 | NS_IMETHOD Run() |
michael@0 | 8604 | { |
michael@0 | 8605 | nsGlobalWindow::RunPendingTimeoutsRecursive(mWindow, mWindow); |
michael@0 | 8606 | |
michael@0 | 8607 | return NS_OK; |
michael@0 | 8608 | } |
michael@0 | 8609 | |
michael@0 | 8610 | private: |
michael@0 | 8611 | nsRefPtr<nsGlobalWindow> mWindow; |
michael@0 | 8612 | }; |
michael@0 | 8613 | |
michael@0 | 8614 | void |
michael@0 | 8615 | nsGlobalWindow::LeaveModalState() |
michael@0 | 8616 | { |
michael@0 | 8617 | MOZ_ASSERT(IsOuterWindow(), "Modal state is maintained on outer windows"); |
michael@0 | 8618 | |
michael@0 | 8619 | nsGlobalWindow* topWin = GetScriptableTop(); |
michael@0 | 8620 | |
michael@0 | 8621 | if (!topWin) { |
michael@0 | 8622 | NS_ERROR("Uh, LeaveModalState() called w/o a reachable top window?"); |
michael@0 | 8623 | return; |
michael@0 | 8624 | } |
michael@0 | 8625 | |
michael@0 | 8626 | topWin->mModalStateDepth--; |
michael@0 | 8627 | |
michael@0 | 8628 | if (topWin->mModalStateDepth == 0) { |
michael@0 | 8629 | nsCOMPtr<nsIRunnable> runner = new nsPendingTimeoutRunner(topWin); |
michael@0 | 8630 | if (NS_FAILED(NS_DispatchToCurrentThread(runner))) |
michael@0 | 8631 | NS_WARNING("failed to dispatch pending timeout runnable"); |
michael@0 | 8632 | |
michael@0 | 8633 | if (mSuspendedDoc) { |
michael@0 | 8634 | nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc(); |
michael@0 | 8635 | mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eAnimationsOnly, |
michael@0 | 8636 | currentDoc == mSuspendedDoc); |
michael@0 | 8637 | mSuspendedDoc = nullptr; |
michael@0 | 8638 | } |
michael@0 | 8639 | } |
michael@0 | 8640 | |
michael@0 | 8641 | // Remember the time of the last dialog quit. |
michael@0 | 8642 | nsGlobalWindow *inner = topWin->GetCurrentInnerWindowInternal(); |
michael@0 | 8643 | if (inner) |
michael@0 | 8644 | inner->mLastDialogQuitTime = TimeStamp::Now(); |
michael@0 | 8645 | } |
michael@0 | 8646 | |
michael@0 | 8647 | bool |
michael@0 | 8648 | nsGlobalWindow::IsInModalState() |
michael@0 | 8649 | { |
michael@0 | 8650 | nsGlobalWindow *topWin = GetScriptableTop(); |
michael@0 | 8651 | |
michael@0 | 8652 | if (!topWin) { |
michael@0 | 8653 | NS_ERROR("Uh, IsInModalState() called w/o a reachable top window?"); |
michael@0 | 8654 | |
michael@0 | 8655 | return false; |
michael@0 | 8656 | } |
michael@0 | 8657 | |
michael@0 | 8658 | return topWin->mModalStateDepth != 0; |
michael@0 | 8659 | } |
michael@0 | 8660 | |
michael@0 | 8661 | // static |
michael@0 | 8662 | void |
michael@0 | 8663 | nsGlobalWindow::NotifyDOMWindowDestroyed(nsGlobalWindow* aWindow) { |
michael@0 | 8664 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 8665 | services::GetObserverService(); |
michael@0 | 8666 | if (observerService) { |
michael@0 | 8667 | observerService-> |
michael@0 | 8668 | NotifyObservers(ToSupports(aWindow), |
michael@0 | 8669 | DOM_WINDOW_DESTROYED_TOPIC, nullptr); |
michael@0 | 8670 | } |
michael@0 | 8671 | } |
michael@0 | 8672 | |
michael@0 | 8673 | class WindowDestroyedEvent : public nsRunnable |
michael@0 | 8674 | { |
michael@0 | 8675 | public: |
michael@0 | 8676 | WindowDestroyedEvent(nsPIDOMWindow* aWindow, uint64_t aID, |
michael@0 | 8677 | const char* aTopic) : |
michael@0 | 8678 | mID(aID), mTopic(aTopic) |
michael@0 | 8679 | { |
michael@0 | 8680 | mWindow = do_GetWeakReference(aWindow); |
michael@0 | 8681 | } |
michael@0 | 8682 | |
michael@0 | 8683 | NS_IMETHOD Run() |
michael@0 | 8684 | { |
michael@0 | 8685 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 8686 | do_GetService("@mozilla.org/observer-service;1"); |
michael@0 | 8687 | if (observerService) { |
michael@0 | 8688 | nsCOMPtr<nsISupportsPRUint64> wrapper = |
michael@0 | 8689 | do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID); |
michael@0 | 8690 | if (wrapper) { |
michael@0 | 8691 | wrapper->SetData(mID); |
michael@0 | 8692 | observerService->NotifyObservers(wrapper, mTopic.get(), nullptr); |
michael@0 | 8693 | } |
michael@0 | 8694 | } |
michael@0 | 8695 | |
michael@0 | 8696 | bool skipNukeCrossCompartment = false; |
michael@0 | 8697 | #ifndef DEBUG |
michael@0 | 8698 | nsCOMPtr<nsIAppStartup> appStartup = |
michael@0 | 8699 | do_GetService(NS_APPSTARTUP_CONTRACTID); |
michael@0 | 8700 | |
michael@0 | 8701 | if (appStartup) { |
michael@0 | 8702 | appStartup->GetShuttingDown(&skipNukeCrossCompartment); |
michael@0 | 8703 | } |
michael@0 | 8704 | #endif |
michael@0 | 8705 | |
michael@0 | 8706 | nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow); |
michael@0 | 8707 | if (!skipNukeCrossCompartment && window) { |
michael@0 | 8708 | nsGlobalWindow* currentInner = |
michael@0 | 8709 | window->IsInnerWindow() ? static_cast<nsGlobalWindow*>(window.get()) : |
michael@0 | 8710 | static_cast<nsGlobalWindow*>(window->GetCurrentInnerWindow()); |
michael@0 | 8711 | NS_ENSURE_TRUE(currentInner, NS_OK); |
michael@0 | 8712 | |
michael@0 | 8713 | AutoSafeJSContext cx; |
michael@0 | 8714 | JS::Rooted<JSObject*> obj(cx, currentInner->FastGetGlobalJSObject()); |
michael@0 | 8715 | // We only want to nuke wrappers for the chrome->content case |
michael@0 | 8716 | if (obj && !js::IsSystemCompartment(js::GetObjectCompartment(obj))) { |
michael@0 | 8717 | js::NukeCrossCompartmentWrappers(cx, |
michael@0 | 8718 | js::ChromeCompartmentsOnly(), |
michael@0 | 8719 | js::SingleCompartment(js::GetObjectCompartment(obj)), |
michael@0 | 8720 | window->IsInnerWindow() ? js::DontNukeWindowReferences : |
michael@0 | 8721 | js::NukeWindowReferences); |
michael@0 | 8722 | } |
michael@0 | 8723 | } |
michael@0 | 8724 | |
michael@0 | 8725 | return NS_OK; |
michael@0 | 8726 | } |
michael@0 | 8727 | |
michael@0 | 8728 | private: |
michael@0 | 8729 | uint64_t mID; |
michael@0 | 8730 | nsCString mTopic; |
michael@0 | 8731 | nsWeakPtr mWindow; |
michael@0 | 8732 | }; |
michael@0 | 8733 | |
michael@0 | 8734 | void |
michael@0 | 8735 | nsGlobalWindow::NotifyWindowIDDestroyed(const char* aTopic) |
michael@0 | 8736 | { |
michael@0 | 8737 | nsRefPtr<nsIRunnable> runnable = new WindowDestroyedEvent(this, mWindowID, aTopic); |
michael@0 | 8738 | nsresult rv = NS_DispatchToCurrentThread(runnable); |
michael@0 | 8739 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 8740 | mNotifiedIDDestroyed = true; |
michael@0 | 8741 | } |
michael@0 | 8742 | } |
michael@0 | 8743 | |
michael@0 | 8744 | // static |
michael@0 | 8745 | void |
michael@0 | 8746 | nsGlobalWindow::NotifyDOMWindowFrozen(nsGlobalWindow* aWindow) { |
michael@0 | 8747 | if (aWindow && aWindow->IsInnerWindow()) { |
michael@0 | 8748 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 8749 | services::GetObserverService(); |
michael@0 | 8750 | if (observerService) { |
michael@0 | 8751 | observerService-> |
michael@0 | 8752 | NotifyObservers(ToSupports(aWindow), |
michael@0 | 8753 | DOM_WINDOW_FROZEN_TOPIC, nullptr); |
michael@0 | 8754 | } |
michael@0 | 8755 | } |
michael@0 | 8756 | } |
michael@0 | 8757 | |
michael@0 | 8758 | // static |
michael@0 | 8759 | void |
michael@0 | 8760 | nsGlobalWindow::NotifyDOMWindowThawed(nsGlobalWindow* aWindow) { |
michael@0 | 8761 | if (aWindow && aWindow->IsInnerWindow()) { |
michael@0 | 8762 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 8763 | services::GetObserverService(); |
michael@0 | 8764 | if (observerService) { |
michael@0 | 8765 | observerService-> |
michael@0 | 8766 | NotifyObservers(ToSupports(aWindow), |
michael@0 | 8767 | DOM_WINDOW_THAWED_TOPIC, nullptr); |
michael@0 | 8768 | } |
michael@0 | 8769 | } |
michael@0 | 8770 | } |
michael@0 | 8771 | |
michael@0 | 8772 | JSObject* |
michael@0 | 8773 | nsGlobalWindow::GetCachedXBLPrototypeHandler(nsXBLPrototypeHandler* aKey) |
michael@0 | 8774 | { |
michael@0 | 8775 | AutoSafeJSContext cx; |
michael@0 | 8776 | JS::Rooted<JSObject*> handler(cx); |
michael@0 | 8777 | if (mCachedXBLPrototypeHandlers) { |
michael@0 | 8778 | mCachedXBLPrototypeHandlers->Get(aKey, handler.address()); |
michael@0 | 8779 | } |
michael@0 | 8780 | return handler; |
michael@0 | 8781 | } |
michael@0 | 8782 | |
michael@0 | 8783 | void |
michael@0 | 8784 | nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey, |
michael@0 | 8785 | JS::Handle<JSObject*> aHandler) |
michael@0 | 8786 | { |
michael@0 | 8787 | if (!mCachedXBLPrototypeHandlers) { |
michael@0 | 8788 | mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>(); |
michael@0 | 8789 | PreserveWrapper(ToSupports(this)); |
michael@0 | 8790 | } |
michael@0 | 8791 | |
michael@0 | 8792 | mCachedXBLPrototypeHandlers->Put(aKey, aHandler); |
michael@0 | 8793 | } |
michael@0 | 8794 | |
michael@0 | 8795 | /** |
michael@0 | 8796 | * GetScriptableFrameElement is called when script reads |
michael@0 | 8797 | * nsIGlobalWindow::frameElement. |
michael@0 | 8798 | * |
michael@0 | 8799 | * In contrast to GetRealFrameElement, GetScriptableFrameElement says that the |
michael@0 | 8800 | * window contained by an <iframe mozbrowser> or <iframe mozapp> has no frame |
michael@0 | 8801 | * element (effectively treating a mozbrowser the same as a content/chrome |
michael@0 | 8802 | * boundary). |
michael@0 | 8803 | */ |
michael@0 | 8804 | NS_IMETHODIMP |
michael@0 | 8805 | nsGlobalWindow::GetScriptableFrameElement(nsIDOMElement** aFrameElement) |
michael@0 | 8806 | { |
michael@0 | 8807 | ErrorResult rv; |
michael@0 | 8808 | nsCOMPtr<nsIDOMElement> frameElement = do_QueryInterface(GetFrameElement(rv)); |
michael@0 | 8809 | if (rv.Failed()) { |
michael@0 | 8810 | return rv.ErrorCode(); |
michael@0 | 8811 | } |
michael@0 | 8812 | |
michael@0 | 8813 | frameElement.forget(aFrameElement); |
michael@0 | 8814 | |
michael@0 | 8815 | return NS_OK; |
michael@0 | 8816 | } |
michael@0 | 8817 | |
michael@0 | 8818 | Element* |
michael@0 | 8819 | nsGlobalWindow::GetFrameElement(ErrorResult& aError) |
michael@0 | 8820 | { |
michael@0 | 8821 | FORWARD_TO_OUTER_OR_THROW(GetFrameElement, (aError), aError, nullptr); |
michael@0 | 8822 | |
michael@0 | 8823 | if (!mDocShell || mDocShell->GetIsBrowserOrApp()) { |
michael@0 | 8824 | return nullptr; |
michael@0 | 8825 | } |
michael@0 | 8826 | |
michael@0 | 8827 | // Per HTML5, the frameElement getter returns null in cross-origin situations. |
michael@0 | 8828 | Element* element = GetRealFrameElement(aError); |
michael@0 | 8829 | if (aError.Failed() || !element) { |
michael@0 | 8830 | return nullptr; |
michael@0 | 8831 | } |
michael@0 | 8832 | if (!nsContentUtils::GetSubjectPrincipal()-> |
michael@0 | 8833 | SubsumesConsideringDomain(element->NodePrincipal())) { |
michael@0 | 8834 | return nullptr; |
michael@0 | 8835 | } |
michael@0 | 8836 | return element; |
michael@0 | 8837 | } |
michael@0 | 8838 | |
michael@0 | 8839 | Element* |
michael@0 | 8840 | nsGlobalWindow::GetRealFrameElement(ErrorResult& aError) |
michael@0 | 8841 | { |
michael@0 | 8842 | FORWARD_TO_OUTER_OR_THROW(GetRealFrameElement, (aError), aError, nullptr); |
michael@0 | 8843 | |
michael@0 | 8844 | if (!mDocShell) { |
michael@0 | 8845 | return nullptr; |
michael@0 | 8846 | } |
michael@0 | 8847 | |
michael@0 | 8848 | nsCOMPtr<nsIDocShell> parent; |
michael@0 | 8849 | mDocShell->GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)); |
michael@0 | 8850 | |
michael@0 | 8851 | if (!parent || parent == mDocShell) { |
michael@0 | 8852 | // We're at a chrome boundary, don't expose the chrome iframe |
michael@0 | 8853 | // element to content code. |
michael@0 | 8854 | return nullptr; |
michael@0 | 8855 | } |
michael@0 | 8856 | |
michael@0 | 8857 | return mFrameElement; |
michael@0 | 8858 | } |
michael@0 | 8859 | |
michael@0 | 8860 | /** |
michael@0 | 8861 | * nsIGlobalWindow::GetFrameElement (when called from C++) is just a wrapper |
michael@0 | 8862 | * around GetRealFrameElement. |
michael@0 | 8863 | */ |
michael@0 | 8864 | NS_IMETHODIMP |
michael@0 | 8865 | nsGlobalWindow::GetRealFrameElement(nsIDOMElement** aFrameElement) |
michael@0 | 8866 | { |
michael@0 | 8867 | ErrorResult rv; |
michael@0 | 8868 | nsCOMPtr<nsIDOMElement> frameElement = |
michael@0 | 8869 | do_QueryInterface(GetRealFrameElement(rv)); |
michael@0 | 8870 | frameElement.forget(aFrameElement); |
michael@0 | 8871 | |
michael@0 | 8872 | return rv.ErrorCode(); |
michael@0 | 8873 | } |
michael@0 | 8874 | |
michael@0 | 8875 | // Helper for converting window.showModalDialog() options (list of ';' |
michael@0 | 8876 | // separated name (:|=) value pairs) to a format that's parsable by |
michael@0 | 8877 | // our normal window opening code. |
michael@0 | 8878 | |
michael@0 | 8879 | void |
michael@0 | 8880 | ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult) |
michael@0 | 8881 | { |
michael@0 | 8882 | nsAString::const_iterator end; |
michael@0 | 8883 | aOptions.EndReading(end); |
michael@0 | 8884 | |
michael@0 | 8885 | nsAString::const_iterator iter; |
michael@0 | 8886 | aOptions.BeginReading(iter); |
michael@0 | 8887 | |
michael@0 | 8888 | while (iter != end) { |
michael@0 | 8889 | // Skip whitespace. |
michael@0 | 8890 | while (nsCRT::IsAsciiSpace(*iter) && iter != end) { |
michael@0 | 8891 | ++iter; |
michael@0 | 8892 | } |
michael@0 | 8893 | |
michael@0 | 8894 | nsAString::const_iterator name_start = iter; |
michael@0 | 8895 | |
michael@0 | 8896 | // Skip characters until we find whitespace, ';', ':', or '=' |
michael@0 | 8897 | while (iter != end && !nsCRT::IsAsciiSpace(*iter) && |
michael@0 | 8898 | *iter != ';' && |
michael@0 | 8899 | *iter != ':' && |
michael@0 | 8900 | *iter != '=') { |
michael@0 | 8901 | ++iter; |
michael@0 | 8902 | } |
michael@0 | 8903 | |
michael@0 | 8904 | nsAString::const_iterator name_end = iter; |
michael@0 | 8905 | |
michael@0 | 8906 | // Skip whitespace. |
michael@0 | 8907 | while (nsCRT::IsAsciiSpace(*iter) && iter != end) { |
michael@0 | 8908 | ++iter; |
michael@0 | 8909 | } |
michael@0 | 8910 | |
michael@0 | 8911 | if (*iter == ';') { |
michael@0 | 8912 | // No value found, skip the ';' and keep going. |
michael@0 | 8913 | ++iter; |
michael@0 | 8914 | |
michael@0 | 8915 | continue; |
michael@0 | 8916 | } |
michael@0 | 8917 | |
michael@0 | 8918 | nsAString::const_iterator value_start = iter; |
michael@0 | 8919 | nsAString::const_iterator value_end = iter; |
michael@0 | 8920 | |
michael@0 | 8921 | if (*iter == ':' || *iter == '=') { |
michael@0 | 8922 | // We found name followed by ':' or '='. Look for a value. |
michael@0 | 8923 | |
michael@0 | 8924 | iter++; // Skip the ':' or '=' |
michael@0 | 8925 | |
michael@0 | 8926 | // Skip whitespace. |
michael@0 | 8927 | while (nsCRT::IsAsciiSpace(*iter) && iter != end) { |
michael@0 | 8928 | ++iter; |
michael@0 | 8929 | } |
michael@0 | 8930 | |
michael@0 | 8931 | value_start = iter; |
michael@0 | 8932 | |
michael@0 | 8933 | // Skip until we find whitespace, or ';'. |
michael@0 | 8934 | while (iter != end && !nsCRT::IsAsciiSpace(*iter) && |
michael@0 | 8935 | *iter != ';') { |
michael@0 | 8936 | ++iter; |
michael@0 | 8937 | } |
michael@0 | 8938 | |
michael@0 | 8939 | value_end = iter; |
michael@0 | 8940 | |
michael@0 | 8941 | // Skip whitespace. |
michael@0 | 8942 | while (nsCRT::IsAsciiSpace(*iter) && iter != end) { |
michael@0 | 8943 | ++iter; |
michael@0 | 8944 | } |
michael@0 | 8945 | } |
michael@0 | 8946 | |
michael@0 | 8947 | const nsDependentSubstring& name = Substring(name_start, name_end); |
michael@0 | 8948 | const nsDependentSubstring& value = Substring(value_start, value_end); |
michael@0 | 8949 | |
michael@0 | 8950 | if (name.LowerCaseEqualsLiteral("center")) { |
michael@0 | 8951 | if (value.LowerCaseEqualsLiteral("on") || |
michael@0 | 8952 | value.LowerCaseEqualsLiteral("yes") || |
michael@0 | 8953 | value.LowerCaseEqualsLiteral("1")) { |
michael@0 | 8954 | aResult.AppendLiteral(",centerscreen=1"); |
michael@0 | 8955 | } |
michael@0 | 8956 | } else if (name.LowerCaseEqualsLiteral("dialogwidth")) { |
michael@0 | 8957 | if (!value.IsEmpty()) { |
michael@0 | 8958 | aResult.AppendLiteral(",width="); |
michael@0 | 8959 | aResult.Append(value); |
michael@0 | 8960 | } |
michael@0 | 8961 | } else if (name.LowerCaseEqualsLiteral("dialogheight")) { |
michael@0 | 8962 | if (!value.IsEmpty()) { |
michael@0 | 8963 | aResult.AppendLiteral(",height="); |
michael@0 | 8964 | aResult.Append(value); |
michael@0 | 8965 | } |
michael@0 | 8966 | } else if (name.LowerCaseEqualsLiteral("dialogtop")) { |
michael@0 | 8967 | if (!value.IsEmpty()) { |
michael@0 | 8968 | aResult.AppendLiteral(",top="); |
michael@0 | 8969 | aResult.Append(value); |
michael@0 | 8970 | } |
michael@0 | 8971 | } else if (name.LowerCaseEqualsLiteral("dialogleft")) { |
michael@0 | 8972 | if (!value.IsEmpty()) { |
michael@0 | 8973 | aResult.AppendLiteral(",left="); |
michael@0 | 8974 | aResult.Append(value); |
michael@0 | 8975 | } |
michael@0 | 8976 | } else if (name.LowerCaseEqualsLiteral("resizable")) { |
michael@0 | 8977 | if (value.LowerCaseEqualsLiteral("on") || |
michael@0 | 8978 | value.LowerCaseEqualsLiteral("yes") || |
michael@0 | 8979 | value.LowerCaseEqualsLiteral("1")) { |
michael@0 | 8980 | aResult.AppendLiteral(",resizable=1"); |
michael@0 | 8981 | } |
michael@0 | 8982 | } else if (name.LowerCaseEqualsLiteral("scroll")) { |
michael@0 | 8983 | if (value.LowerCaseEqualsLiteral("off") || |
michael@0 | 8984 | value.LowerCaseEqualsLiteral("no") || |
michael@0 | 8985 | value.LowerCaseEqualsLiteral("0")) { |
michael@0 | 8986 | aResult.AppendLiteral(",scrollbars=0"); |
michael@0 | 8987 | } |
michael@0 | 8988 | } |
michael@0 | 8989 | |
michael@0 | 8990 | if (iter == end) { |
michael@0 | 8991 | break; |
michael@0 | 8992 | } |
michael@0 | 8993 | |
michael@0 | 8994 | iter++; |
michael@0 | 8995 | } |
michael@0 | 8996 | } |
michael@0 | 8997 | |
michael@0 | 8998 | already_AddRefed<nsIVariant> |
michael@0 | 8999 | nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument, |
michael@0 | 9000 | const nsAString& aOptions, ErrorResult& aError) |
michael@0 | 9001 | { |
michael@0 | 9002 | if (mDoc) { |
michael@0 | 9003 | mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog); |
michael@0 | 9004 | } |
michael@0 | 9005 | |
michael@0 | 9006 | FORWARD_TO_OUTER_OR_THROW(ShowModalDialog, |
michael@0 | 9007 | (aUrl, aArgument, aOptions, aError), aError, |
michael@0 | 9008 | nullptr); |
michael@0 | 9009 | |
michael@0 | 9010 | if (Preferences::GetBool("dom.disable_window_showModalDialog", false)) { |
michael@0 | 9011 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 9012 | return nullptr; |
michael@0 | 9013 | } |
michael@0 | 9014 | |
michael@0 | 9015 | nsRefPtr<DialogValueHolder> argHolder = |
michael@0 | 9016 | new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), aArgument); |
michael@0 | 9017 | |
michael@0 | 9018 | // Before bringing up the window/dialog, unsuppress painting and flush |
michael@0 | 9019 | // pending reflows. |
michael@0 | 9020 | EnsureReflowFlushAndPaint(); |
michael@0 | 9021 | |
michael@0 | 9022 | if (!AreDialogsEnabled()) { |
michael@0 | 9023 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 9024 | return nullptr; |
michael@0 | 9025 | } |
michael@0 | 9026 | |
michael@0 | 9027 | if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) { |
michael@0 | 9028 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 9029 | return nullptr; |
michael@0 | 9030 | } |
michael@0 | 9031 | |
michael@0 | 9032 | nsCOMPtr<nsIDOMWindow> dlgWin; |
michael@0 | 9033 | nsAutoString options(NS_LITERAL_STRING("-moz-internal-modal=1,status=1")); |
michael@0 | 9034 | |
michael@0 | 9035 | ConvertDialogOptions(aOptions, options); |
michael@0 | 9036 | |
michael@0 | 9037 | options.AppendLiteral(",scrollbars=1,centerscreen=1,resizable=0"); |
michael@0 | 9038 | |
michael@0 | 9039 | EnterModalState(); |
michael@0 | 9040 | uint32_t oldMicroTaskLevel = nsContentUtils::MicroTaskLevel(); |
michael@0 | 9041 | nsContentUtils::SetMicroTaskLevel(0); |
michael@0 | 9042 | aError = OpenInternal(aUrl, EmptyString(), options, |
michael@0 | 9043 | false, // aDialog |
michael@0 | 9044 | true, // aContentModal |
michael@0 | 9045 | true, // aCalledNoScript |
michael@0 | 9046 | true, // aDoJSFixups |
michael@0 | 9047 | true, // aNavigate |
michael@0 | 9048 | nullptr, argHolder, // args |
michael@0 | 9049 | GetPrincipal(), // aCalleePrincipal |
michael@0 | 9050 | nullptr, // aJSCallerContext |
michael@0 | 9051 | getter_AddRefs(dlgWin)); |
michael@0 | 9052 | nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel); |
michael@0 | 9053 | LeaveModalState(); |
michael@0 | 9054 | if (aError.Failed()) { |
michael@0 | 9055 | return nullptr; |
michael@0 | 9056 | } |
michael@0 | 9057 | |
michael@0 | 9058 | nsCOMPtr<nsIDOMModalContentWindow> dialog = do_QueryInterface(dlgWin); |
michael@0 | 9059 | if (!dialog) { |
michael@0 | 9060 | return nullptr; |
michael@0 | 9061 | } |
michael@0 | 9062 | |
michael@0 | 9063 | nsCOMPtr<nsIVariant> retVal; |
michael@0 | 9064 | aError = dialog->GetReturnValue(getter_AddRefs(retVal)); |
michael@0 | 9065 | MOZ_ASSERT(!aError.Failed()); |
michael@0 | 9066 | |
michael@0 | 9067 | return retVal.forget(); |
michael@0 | 9068 | } |
michael@0 | 9069 | |
michael@0 | 9070 | void |
michael@0 | 9071 | nsGlobalWindow::ShowModalDialog(JSContext* aCx, const nsAString& aUrl, |
michael@0 | 9072 | JS::Handle<JS::Value> aArgument, |
michael@0 | 9073 | const nsAString& aOptions, |
michael@0 | 9074 | JS::MutableHandle<JS::Value> aRetval, |
michael@0 | 9075 | ErrorResult& aError) |
michael@0 | 9076 | { |
michael@0 | 9077 | nsCOMPtr<nsIVariant> args; |
michael@0 | 9078 | aError = nsContentUtils::XPConnect()->JSToVariant(aCx, |
michael@0 | 9079 | aArgument, |
michael@0 | 9080 | getter_AddRefs(args)); |
michael@0 | 9081 | |
michael@0 | 9082 | nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aUrl, args, aOptions, aError); |
michael@0 | 9083 | if (aError.Failed()) { |
michael@0 | 9084 | return; |
michael@0 | 9085 | } |
michael@0 | 9086 | |
michael@0 | 9087 | JS::Rooted<JS::Value> result(aCx); |
michael@0 | 9088 | if (retVal) { |
michael@0 | 9089 | aError = nsContentUtils::XPConnect()->VariantToJS(aCx, |
michael@0 | 9090 | FastGetGlobalJSObject(), |
michael@0 | 9091 | retVal, aRetval); |
michael@0 | 9092 | } else { |
michael@0 | 9093 | aRetval.setNull(); |
michael@0 | 9094 | } |
michael@0 | 9095 | } |
michael@0 | 9096 | |
michael@0 | 9097 | NS_IMETHODIMP |
michael@0 | 9098 | nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs_, |
michael@0 | 9099 | const nsAString& aOptions, uint8_t aArgc, |
michael@0 | 9100 | nsIVariant **aRetVal) |
michael@0 | 9101 | { |
michael@0 | 9102 | // Per-spec the |arguments| parameter is supposed to pass through unmodified. |
michael@0 | 9103 | // However, XPConnect default-initializes variants to null, rather than |
michael@0 | 9104 | // undefined. Fix this up here. |
michael@0 | 9105 | nsCOMPtr<nsIVariant> aArgs = aArgs_; |
michael@0 | 9106 | if (aArgc < 1) { |
michael@0 | 9107 | aArgs = CreateVoidVariant(); |
michael@0 | 9108 | } |
michael@0 | 9109 | |
michael@0 | 9110 | ErrorResult rv; |
michael@0 | 9111 | nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aURI, aArgs, aOptions, rv); |
michael@0 | 9112 | retVal.forget(aRetVal); |
michael@0 | 9113 | |
michael@0 | 9114 | return rv.ErrorCode(); |
michael@0 | 9115 | } |
michael@0 | 9116 | |
michael@0 | 9117 | class CommandDispatcher : public nsRunnable |
michael@0 | 9118 | { |
michael@0 | 9119 | public: |
michael@0 | 9120 | CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher, |
michael@0 | 9121 | const nsAString& aAction) |
michael@0 | 9122 | : mDispatcher(aDispatcher), mAction(aAction) {} |
michael@0 | 9123 | |
michael@0 | 9124 | NS_IMETHOD Run() |
michael@0 | 9125 | { |
michael@0 | 9126 | return mDispatcher->UpdateCommands(mAction); |
michael@0 | 9127 | } |
michael@0 | 9128 | |
michael@0 | 9129 | nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher; |
michael@0 | 9130 | nsString mAction; |
michael@0 | 9131 | }; |
michael@0 | 9132 | |
michael@0 | 9133 | NS_IMETHODIMP |
michael@0 | 9134 | nsGlobalWindow::UpdateCommands(const nsAString& anAction) |
michael@0 | 9135 | { |
michael@0 | 9136 | nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot(); |
michael@0 | 9137 | if (!rootWindow) |
michael@0 | 9138 | return NS_OK; |
michael@0 | 9139 | |
michael@0 | 9140 | nsCOMPtr<nsIDOMXULDocument> xulDoc = |
michael@0 | 9141 | do_QueryInterface(rootWindow->GetExtantDoc()); |
michael@0 | 9142 | // See if we contain a XUL document. |
michael@0 | 9143 | if (xulDoc) { |
michael@0 | 9144 | // Retrieve the command dispatcher and call updateCommands on it. |
michael@0 | 9145 | nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher; |
michael@0 | 9146 | xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher)); |
michael@0 | 9147 | if (xulCommandDispatcher) { |
michael@0 | 9148 | nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher, |
michael@0 | 9149 | anAction)); |
michael@0 | 9150 | } |
michael@0 | 9151 | } |
michael@0 | 9152 | |
michael@0 | 9153 | return NS_OK; |
michael@0 | 9154 | } |
michael@0 | 9155 | |
michael@0 | 9156 | Selection* |
michael@0 | 9157 | nsGlobalWindow::GetSelection(ErrorResult& aError) |
michael@0 | 9158 | { |
michael@0 | 9159 | FORWARD_TO_OUTER_OR_THROW(GetSelection, (aError), aError, nullptr); |
michael@0 | 9160 | |
michael@0 | 9161 | if (!mDocShell) { |
michael@0 | 9162 | return nullptr; |
michael@0 | 9163 | } |
michael@0 | 9164 | |
michael@0 | 9165 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 9166 | if (!presShell) { |
michael@0 | 9167 | return nullptr; |
michael@0 | 9168 | } |
michael@0 | 9169 | |
michael@0 | 9170 | return static_cast<Selection*>(presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL)); |
michael@0 | 9171 | } |
michael@0 | 9172 | |
michael@0 | 9173 | NS_IMETHODIMP |
michael@0 | 9174 | nsGlobalWindow::GetSelection(nsISelection** aSelection) |
michael@0 | 9175 | { |
michael@0 | 9176 | ErrorResult rv; |
michael@0 | 9177 | nsCOMPtr<nsISelection> selection = GetSelection(rv); |
michael@0 | 9178 | selection.forget(aSelection); |
michael@0 | 9179 | |
michael@0 | 9180 | return rv.ErrorCode(); |
michael@0 | 9181 | } |
michael@0 | 9182 | |
michael@0 | 9183 | bool |
michael@0 | 9184 | nsGlobalWindow::Find(const nsAString& aString, bool aCaseSensitive, |
michael@0 | 9185 | bool aBackwards, bool aWrapAround, bool aWholeWord, |
michael@0 | 9186 | bool aSearchInFrames, bool aShowDialog, |
michael@0 | 9187 | ErrorResult& aError) |
michael@0 | 9188 | { |
michael@0 | 9189 | if (Preferences::GetBool("dom.disable_window_find", false)) { |
michael@0 | 9190 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 9191 | return false; |
michael@0 | 9192 | } |
michael@0 | 9193 | |
michael@0 | 9194 | FORWARD_TO_OUTER_OR_THROW(Find, |
michael@0 | 9195 | (aString, aCaseSensitive, aBackwards, aWrapAround, |
michael@0 | 9196 | aWholeWord, aSearchInFrames, aShowDialog, aError), |
michael@0 | 9197 | aError, false); |
michael@0 | 9198 | |
michael@0 | 9199 | nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(mDocShell)); |
michael@0 | 9200 | if (!finder) { |
michael@0 | 9201 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 9202 | return false; |
michael@0 | 9203 | } |
michael@0 | 9204 | |
michael@0 | 9205 | // Set the options of the search |
michael@0 | 9206 | aError = finder->SetSearchString(PromiseFlatString(aString).get()); |
michael@0 | 9207 | if (aError.Failed()) { |
michael@0 | 9208 | return false; |
michael@0 | 9209 | } |
michael@0 | 9210 | finder->SetMatchCase(aCaseSensitive); |
michael@0 | 9211 | finder->SetFindBackwards(aBackwards); |
michael@0 | 9212 | finder->SetWrapFind(aWrapAround); |
michael@0 | 9213 | finder->SetEntireWord(aWholeWord); |
michael@0 | 9214 | finder->SetSearchFrames(aSearchInFrames); |
michael@0 | 9215 | |
michael@0 | 9216 | // the nsIWebBrowserFind is initialized to use this window |
michael@0 | 9217 | // as the search root, but uses focus to set the current search |
michael@0 | 9218 | // frame. If we're being called from JS (as here), this window |
michael@0 | 9219 | // should be the current search frame. |
michael@0 | 9220 | nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder)); |
michael@0 | 9221 | if (framesFinder) { |
michael@0 | 9222 | framesFinder->SetRootSearchFrame(this); // paranoia |
michael@0 | 9223 | framesFinder->SetCurrentSearchFrame(this); |
michael@0 | 9224 | } |
michael@0 | 9225 | |
michael@0 | 9226 | // The Find API does not accept empty strings. Launch the Find Dialog. |
michael@0 | 9227 | if (aString.IsEmpty() || aShowDialog) { |
michael@0 | 9228 | // See if the find dialog is already up using nsIWindowMediator |
michael@0 | 9229 | nsCOMPtr<nsIWindowMediator> windowMediator = |
michael@0 | 9230 | do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); |
michael@0 | 9231 | |
michael@0 | 9232 | nsCOMPtr<nsIDOMWindow> findDialog; |
michael@0 | 9233 | |
michael@0 | 9234 | if (windowMediator) { |
michael@0 | 9235 | windowMediator->GetMostRecentWindow(MOZ_UTF16("findInPage"), |
michael@0 | 9236 | getter_AddRefs(findDialog)); |
michael@0 | 9237 | } |
michael@0 | 9238 | |
michael@0 | 9239 | if (findDialog) { |
michael@0 | 9240 | // The Find dialog is already open, bring it to the top. |
michael@0 | 9241 | aError = findDialog->Focus(); |
michael@0 | 9242 | } else if (finder) { |
michael@0 | 9243 | // Open a Find dialog |
michael@0 | 9244 | nsCOMPtr<nsIDOMWindow> dialog; |
michael@0 | 9245 | aError = OpenDialog(NS_LITERAL_STRING("chrome://global/content/finddialog.xul"), |
michael@0 | 9246 | NS_LITERAL_STRING("_blank"), |
michael@0 | 9247 | NS_LITERAL_STRING("chrome, resizable=no, dependent=yes"), |
michael@0 | 9248 | finder, getter_AddRefs(dialog)); |
michael@0 | 9249 | } |
michael@0 | 9250 | |
michael@0 | 9251 | return false; |
michael@0 | 9252 | } |
michael@0 | 9253 | |
michael@0 | 9254 | // Launch the search with the passed in search string |
michael@0 | 9255 | bool didFind = false; |
michael@0 | 9256 | aError = finder->FindNext(&didFind); |
michael@0 | 9257 | return didFind; |
michael@0 | 9258 | } |
michael@0 | 9259 | |
michael@0 | 9260 | NS_IMETHODIMP |
michael@0 | 9261 | nsGlobalWindow::Find(const nsAString& aStr, bool aCaseSensitive, |
michael@0 | 9262 | bool aBackwards, bool aWrapAround, bool aWholeWord, |
michael@0 | 9263 | bool aSearchInFrames, bool aShowDialog, |
michael@0 | 9264 | bool *aDidFind) |
michael@0 | 9265 | { |
michael@0 | 9266 | ErrorResult rv; |
michael@0 | 9267 | *aDidFind = Find(aStr, aCaseSensitive, aBackwards, aWrapAround, aWholeWord, |
michael@0 | 9268 | aSearchInFrames, aShowDialog, rv); |
michael@0 | 9269 | |
michael@0 | 9270 | return rv.ErrorCode(); |
michael@0 | 9271 | } |
michael@0 | 9272 | |
michael@0 | 9273 | void |
michael@0 | 9274 | nsGlobalWindow::Atob(const nsAString& aAsciiBase64String, |
michael@0 | 9275 | nsAString& aBinaryData, ErrorResult& aError) |
michael@0 | 9276 | { |
michael@0 | 9277 | aError = nsContentUtils::Atob(aAsciiBase64String, aBinaryData); |
michael@0 | 9278 | } |
michael@0 | 9279 | |
michael@0 | 9280 | NS_IMETHODIMP |
michael@0 | 9281 | nsGlobalWindow::Atob(const nsAString& aAsciiBase64String, |
michael@0 | 9282 | nsAString& aBinaryData) |
michael@0 | 9283 | { |
michael@0 | 9284 | ErrorResult rv; |
michael@0 | 9285 | Atob(aAsciiBase64String, aBinaryData, rv); |
michael@0 | 9286 | |
michael@0 | 9287 | return rv.ErrorCode(); |
michael@0 | 9288 | } |
michael@0 | 9289 | |
michael@0 | 9290 | void |
michael@0 | 9291 | nsGlobalWindow::Btoa(const nsAString& aBinaryData, |
michael@0 | 9292 | nsAString& aAsciiBase64String, ErrorResult& aError) |
michael@0 | 9293 | { |
michael@0 | 9294 | aError = nsContentUtils::Btoa(aBinaryData, aAsciiBase64String); |
michael@0 | 9295 | } |
michael@0 | 9296 | |
michael@0 | 9297 | NS_IMETHODIMP |
michael@0 | 9298 | nsGlobalWindow::Btoa(const nsAString& aBinaryData, |
michael@0 | 9299 | nsAString& aAsciiBase64String) |
michael@0 | 9300 | { |
michael@0 | 9301 | ErrorResult rv; |
michael@0 | 9302 | Btoa(aBinaryData, aAsciiBase64String, rv); |
michael@0 | 9303 | |
michael@0 | 9304 | return rv.ErrorCode(); |
michael@0 | 9305 | } |
michael@0 | 9306 | |
michael@0 | 9307 | //***************************************************************************** |
michael@0 | 9308 | // nsGlobalWindow::nsIDOMEventTarget |
michael@0 | 9309 | //***************************************************************************** |
michael@0 | 9310 | |
michael@0 | 9311 | NS_IMETHODIMP |
michael@0 | 9312 | nsGlobalWindow::RemoveEventListener(const nsAString& aType, |
michael@0 | 9313 | nsIDOMEventListener* aListener, |
michael@0 | 9314 | bool aUseCapture) |
michael@0 | 9315 | { |
michael@0 | 9316 | if (nsRefPtr<EventListenerManager> elm = GetExistingListenerManager()) { |
michael@0 | 9317 | elm->RemoveEventListener(aType, aListener, aUseCapture); |
michael@0 | 9318 | } |
michael@0 | 9319 | return NS_OK; |
michael@0 | 9320 | } |
michael@0 | 9321 | |
michael@0 | 9322 | NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsGlobalWindow) |
michael@0 | 9323 | |
michael@0 | 9324 | NS_IMETHODIMP |
michael@0 | 9325 | nsGlobalWindow::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal) |
michael@0 | 9326 | { |
michael@0 | 9327 | FORWARD_TO_INNER(DispatchEvent, (aEvent, aRetVal), NS_OK); |
michael@0 | 9328 | |
michael@0 | 9329 | if (!IsCurrentInnerWindow()) { |
michael@0 | 9330 | NS_WARNING("DispatchEvent called on non-current inner window, dropping. " |
michael@0 | 9331 | "Please check the window in the caller instead."); |
michael@0 | 9332 | return NS_ERROR_FAILURE; |
michael@0 | 9333 | } |
michael@0 | 9334 | |
michael@0 | 9335 | if (!mDoc) { |
michael@0 | 9336 | return NS_ERROR_FAILURE; |
michael@0 | 9337 | } |
michael@0 | 9338 | |
michael@0 | 9339 | // Obtain a presentation shell |
michael@0 | 9340 | nsIPresShell *shell = mDoc->GetShell(); |
michael@0 | 9341 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 9342 | if (shell) { |
michael@0 | 9343 | // Retrieve the context |
michael@0 | 9344 | presContext = shell->GetPresContext(); |
michael@0 | 9345 | } |
michael@0 | 9346 | |
michael@0 | 9347 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 9348 | nsresult rv = |
michael@0 | 9349 | EventDispatcher::DispatchDOMEvent(GetOuterWindow(), nullptr, aEvent, |
michael@0 | 9350 | presContext, &status); |
michael@0 | 9351 | |
michael@0 | 9352 | *aRetVal = (status != nsEventStatus_eConsumeNoDefault); |
michael@0 | 9353 | return rv; |
michael@0 | 9354 | } |
michael@0 | 9355 | |
michael@0 | 9356 | NS_IMETHODIMP |
michael@0 | 9357 | nsGlobalWindow::AddEventListener(const nsAString& aType, |
michael@0 | 9358 | nsIDOMEventListener *aListener, |
michael@0 | 9359 | bool aUseCapture, bool aWantsUntrusted, |
michael@0 | 9360 | uint8_t aOptionalArgc) |
michael@0 | 9361 | { |
michael@0 | 9362 | NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1, |
michael@0 | 9363 | "Won't check if this is chrome, you want to set " |
michael@0 | 9364 | "aWantsUntrusted to false or make the aWantsUntrusted " |
michael@0 | 9365 | "explicit by making optional_argc non-zero."); |
michael@0 | 9366 | |
michael@0 | 9367 | if (IsOuterWindow() && mInnerWindow && |
michael@0 | 9368 | !nsContentUtils::CanCallerAccess(mInnerWindow)) { |
michael@0 | 9369 | return NS_ERROR_DOM_SECURITY_ERR; |
michael@0 | 9370 | } |
michael@0 | 9371 | |
michael@0 | 9372 | if (!aWantsUntrusted && |
michael@0 | 9373 | (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) { |
michael@0 | 9374 | aWantsUntrusted = true; |
michael@0 | 9375 | } |
michael@0 | 9376 | |
michael@0 | 9377 | EventListenerManager* manager = GetOrCreateListenerManager(); |
michael@0 | 9378 | NS_ENSURE_STATE(manager); |
michael@0 | 9379 | manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted); |
michael@0 | 9380 | return NS_OK; |
michael@0 | 9381 | } |
michael@0 | 9382 | |
michael@0 | 9383 | void |
michael@0 | 9384 | nsGlobalWindow::AddEventListener(const nsAString& aType, |
michael@0 | 9385 | EventListener* aListener, |
michael@0 | 9386 | bool aUseCapture, |
michael@0 | 9387 | const Nullable<bool>& aWantsUntrusted, |
michael@0 | 9388 | ErrorResult& aRv) |
michael@0 | 9389 | { |
michael@0 | 9390 | if (IsOuterWindow() && mInnerWindow && |
michael@0 | 9391 | !nsContentUtils::CanCallerAccess(mInnerWindow)) { |
michael@0 | 9392 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
michael@0 | 9393 | return; |
michael@0 | 9394 | } |
michael@0 | 9395 | |
michael@0 | 9396 | bool wantsUntrusted; |
michael@0 | 9397 | if (aWantsUntrusted.IsNull()) { |
michael@0 | 9398 | wantsUntrusted = !nsContentUtils::IsChromeDoc(mDoc); |
michael@0 | 9399 | } else { |
michael@0 | 9400 | wantsUntrusted = aWantsUntrusted.Value(); |
michael@0 | 9401 | } |
michael@0 | 9402 | |
michael@0 | 9403 | EventListenerManager* manager = GetOrCreateListenerManager(); |
michael@0 | 9404 | if (!manager) { |
michael@0 | 9405 | aRv.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 9406 | return; |
michael@0 | 9407 | } |
michael@0 | 9408 | manager->AddEventListener(aType, aListener, aUseCapture, wantsUntrusted); |
michael@0 | 9409 | } |
michael@0 | 9410 | |
michael@0 | 9411 | NS_IMETHODIMP |
michael@0 | 9412 | nsGlobalWindow::AddSystemEventListener(const nsAString& aType, |
michael@0 | 9413 | nsIDOMEventListener *aListener, |
michael@0 | 9414 | bool aUseCapture, |
michael@0 | 9415 | bool aWantsUntrusted, |
michael@0 | 9416 | uint8_t aOptionalArgc) |
michael@0 | 9417 | { |
michael@0 | 9418 | NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1, |
michael@0 | 9419 | "Won't check if this is chrome, you want to set " |
michael@0 | 9420 | "aWantsUntrusted to false or make the aWantsUntrusted " |
michael@0 | 9421 | "explicit by making optional_argc non-zero."); |
michael@0 | 9422 | |
michael@0 | 9423 | if (IsOuterWindow() && mInnerWindow && |
michael@0 | 9424 | !nsContentUtils::CanCallerAccess(mInnerWindow)) { |
michael@0 | 9425 | return NS_ERROR_DOM_SECURITY_ERR; |
michael@0 | 9426 | } |
michael@0 | 9427 | |
michael@0 | 9428 | if (!aWantsUntrusted && |
michael@0 | 9429 | (aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) { |
michael@0 | 9430 | aWantsUntrusted = true; |
michael@0 | 9431 | } |
michael@0 | 9432 | |
michael@0 | 9433 | return NS_AddSystemEventListener(this, aType, aListener, aUseCapture, |
michael@0 | 9434 | aWantsUntrusted); |
michael@0 | 9435 | } |
michael@0 | 9436 | |
michael@0 | 9437 | EventListenerManager* |
michael@0 | 9438 | nsGlobalWindow::GetOrCreateListenerManager() |
michael@0 | 9439 | { |
michael@0 | 9440 | FORWARD_TO_INNER_CREATE(GetOrCreateListenerManager, (), nullptr); |
michael@0 | 9441 | |
michael@0 | 9442 | if (!mListenerManager) { |
michael@0 | 9443 | mListenerManager = |
michael@0 | 9444 | new EventListenerManager(static_cast<EventTarget*>(this)); |
michael@0 | 9445 | } |
michael@0 | 9446 | |
michael@0 | 9447 | return mListenerManager; |
michael@0 | 9448 | } |
michael@0 | 9449 | |
michael@0 | 9450 | EventListenerManager* |
michael@0 | 9451 | nsGlobalWindow::GetExistingListenerManager() const |
michael@0 | 9452 | { |
michael@0 | 9453 | FORWARD_TO_INNER(GetExistingListenerManager, (), nullptr); |
michael@0 | 9454 | |
michael@0 | 9455 | return mListenerManager; |
michael@0 | 9456 | } |
michael@0 | 9457 | |
michael@0 | 9458 | nsIScriptContext* |
michael@0 | 9459 | nsGlobalWindow::GetContextForEventHandlers(nsresult* aRv) |
michael@0 | 9460 | { |
michael@0 | 9461 | *aRv = NS_ERROR_UNEXPECTED; |
michael@0 | 9462 | NS_ENSURE_TRUE(!IsInnerWindow() || IsCurrentInnerWindow(), nullptr); |
michael@0 | 9463 | |
michael@0 | 9464 | nsIScriptContext* scx; |
michael@0 | 9465 | if ((scx = GetContext())) { |
michael@0 | 9466 | *aRv = NS_OK; |
michael@0 | 9467 | return scx; |
michael@0 | 9468 | } |
michael@0 | 9469 | return nullptr; |
michael@0 | 9470 | } |
michael@0 | 9471 | |
michael@0 | 9472 | //***************************************************************************** |
michael@0 | 9473 | // nsGlobalWindow::nsPIDOMWindow |
michael@0 | 9474 | //***************************************************************************** |
michael@0 | 9475 | |
michael@0 | 9476 | nsPIDOMWindow* |
michael@0 | 9477 | nsGlobalWindow::GetPrivateParent() |
michael@0 | 9478 | { |
michael@0 | 9479 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 9480 | |
michael@0 | 9481 | nsCOMPtr<nsIDOMWindow> parent; |
michael@0 | 9482 | GetParent(getter_AddRefs(parent)); |
michael@0 | 9483 | |
michael@0 | 9484 | if (static_cast<nsIDOMWindow *>(this) == parent.get()) { |
michael@0 | 9485 | nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler)); |
michael@0 | 9486 | if (!chromeElement) |
michael@0 | 9487 | return nullptr; // This is ok, just means a null parent. |
michael@0 | 9488 | |
michael@0 | 9489 | nsIDocument* doc = chromeElement->GetDocument(); |
michael@0 | 9490 | if (!doc) |
michael@0 | 9491 | return nullptr; // This is ok, just means a null parent. |
michael@0 | 9492 | |
michael@0 | 9493 | return doc->GetWindow(); |
michael@0 | 9494 | } |
michael@0 | 9495 | |
michael@0 | 9496 | if (parent) { |
michael@0 | 9497 | return static_cast<nsGlobalWindow *> |
michael@0 | 9498 | (static_cast<nsIDOMWindow*>(parent.get())); |
michael@0 | 9499 | } |
michael@0 | 9500 | |
michael@0 | 9501 | return nullptr; |
michael@0 | 9502 | } |
michael@0 | 9503 | |
michael@0 | 9504 | nsPIDOMWindow* |
michael@0 | 9505 | nsGlobalWindow::GetPrivateRoot() |
michael@0 | 9506 | { |
michael@0 | 9507 | if (IsInnerWindow()) { |
michael@0 | 9508 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 9509 | if (!outer) { |
michael@0 | 9510 | NS_WARNING("No outer window available!"); |
michael@0 | 9511 | return nullptr; |
michael@0 | 9512 | } |
michael@0 | 9513 | return outer->GetPrivateRoot(); |
michael@0 | 9514 | } |
michael@0 | 9515 | |
michael@0 | 9516 | nsCOMPtr<nsIDOMWindow> top; |
michael@0 | 9517 | GetTop(getter_AddRefs(top)); |
michael@0 | 9518 | |
michael@0 | 9519 | nsCOMPtr<nsIContent> chromeElement(do_QueryInterface(mChromeEventHandler)); |
michael@0 | 9520 | if (chromeElement) { |
michael@0 | 9521 | nsIDocument* doc = chromeElement->GetDocument(); |
michael@0 | 9522 | if (doc) { |
michael@0 | 9523 | nsIDOMWindow *parent = doc->GetWindow(); |
michael@0 | 9524 | if (parent) { |
michael@0 | 9525 | parent->GetTop(getter_AddRefs(top)); |
michael@0 | 9526 | } |
michael@0 | 9527 | } |
michael@0 | 9528 | } |
michael@0 | 9529 | |
michael@0 | 9530 | return static_cast<nsGlobalWindow*>(top.get()); |
michael@0 | 9531 | } |
michael@0 | 9532 | |
michael@0 | 9533 | |
michael@0 | 9534 | nsIDOMLocation* |
michael@0 | 9535 | nsGlobalWindow::GetLocation(ErrorResult& aError) |
michael@0 | 9536 | { |
michael@0 | 9537 | FORWARD_TO_INNER_OR_THROW(GetLocation, (aError), aError, nullptr); |
michael@0 | 9538 | |
michael@0 | 9539 | nsIDocShell *docShell = GetDocShell(); |
michael@0 | 9540 | if (!mLocation && docShell) { |
michael@0 | 9541 | mLocation = new nsLocation(docShell); |
michael@0 | 9542 | } |
michael@0 | 9543 | return mLocation; |
michael@0 | 9544 | } |
michael@0 | 9545 | |
michael@0 | 9546 | NS_IMETHODIMP |
michael@0 | 9547 | nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation) |
michael@0 | 9548 | { |
michael@0 | 9549 | ErrorResult rv; |
michael@0 | 9550 | nsCOMPtr<nsIDOMLocation> location = GetLocation(rv); |
michael@0 | 9551 | location.forget(aLocation); |
michael@0 | 9552 | |
michael@0 | 9553 | return rv.ErrorCode(); |
michael@0 | 9554 | } |
michael@0 | 9555 | |
michael@0 | 9556 | void |
michael@0 | 9557 | nsGlobalWindow::ActivateOrDeactivate(bool aActivate) |
michael@0 | 9558 | { |
michael@0 | 9559 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 9560 | |
michael@0 | 9561 | // Set / unset mIsActive on the top level window, which is used for the |
michael@0 | 9562 | // :-moz-window-inactive pseudoclass, and its sheet (if any). |
michael@0 | 9563 | nsCOMPtr<nsIWidget> mainWidget = GetMainWidget(); |
michael@0 | 9564 | if (!mainWidget) |
michael@0 | 9565 | return; |
michael@0 | 9566 | |
michael@0 | 9567 | // Get the top level widget (if the main widget is a sheet, this will |
michael@0 | 9568 | // be the sheet's top (non-sheet) parent). |
michael@0 | 9569 | nsCOMPtr<nsIWidget> topLevelWidget = mainWidget->GetSheetWindowParent(); |
michael@0 | 9570 | if (!topLevelWidget) { |
michael@0 | 9571 | topLevelWidget = mainWidget; |
michael@0 | 9572 | } |
michael@0 | 9573 | |
michael@0 | 9574 | nsCOMPtr<nsPIDOMWindow> piMainWindow( |
michael@0 | 9575 | do_QueryInterface(static_cast<nsIDOMWindow*>(this))); |
michael@0 | 9576 | piMainWindow->SetActive(aActivate); |
michael@0 | 9577 | |
michael@0 | 9578 | if (mainWidget != topLevelWidget) { |
michael@0 | 9579 | // This is a workaround for the following problem: |
michael@0 | 9580 | // When a window with an open sheet gains or loses focus, only the sheet |
michael@0 | 9581 | // window receives the NS_ACTIVATE/NS_DEACTIVATE event. However the |
michael@0 | 9582 | // styling of the containing top level window also needs to change. We |
michael@0 | 9583 | // get around this by calling nsPIDOMWindow::SetActive() on both windows. |
michael@0 | 9584 | |
michael@0 | 9585 | // Get the top level widget's nsGlobalWindow |
michael@0 | 9586 | nsCOMPtr<nsIDOMWindow> topLevelWindow; |
michael@0 | 9587 | |
michael@0 | 9588 | // widgetListener should be a nsXULWindow |
michael@0 | 9589 | nsIWidgetListener* listener = topLevelWidget->GetWidgetListener(); |
michael@0 | 9590 | if (listener) { |
michael@0 | 9591 | nsCOMPtr<nsIXULWindow> window = listener->GetXULWindow(); |
michael@0 | 9592 | nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(window)); |
michael@0 | 9593 | topLevelWindow = do_GetInterface(req); |
michael@0 | 9594 | } |
michael@0 | 9595 | |
michael@0 | 9596 | if (topLevelWindow) { |
michael@0 | 9597 | nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(topLevelWindow)); |
michael@0 | 9598 | piWin->SetActive(aActivate); |
michael@0 | 9599 | } |
michael@0 | 9600 | } |
michael@0 | 9601 | } |
michael@0 | 9602 | |
michael@0 | 9603 | static bool |
michael@0 | 9604 | NotifyDocumentTree(nsIDocument* aDocument, void* aData) |
michael@0 | 9605 | { |
michael@0 | 9606 | aDocument->EnumerateSubDocuments(NotifyDocumentTree, nullptr); |
michael@0 | 9607 | aDocument->DocumentStatesChanged(NS_DOCUMENT_STATE_WINDOW_INACTIVE); |
michael@0 | 9608 | return true; |
michael@0 | 9609 | } |
michael@0 | 9610 | |
michael@0 | 9611 | void |
michael@0 | 9612 | nsGlobalWindow::SetActive(bool aActive) |
michael@0 | 9613 | { |
michael@0 | 9614 | nsPIDOMWindow::SetActive(aActive); |
michael@0 | 9615 | NotifyDocumentTree(mDoc, nullptr); |
michael@0 | 9616 | } |
michael@0 | 9617 | |
michael@0 | 9618 | void nsGlobalWindow::SetIsBackground(bool aIsBackground) |
michael@0 | 9619 | { |
michael@0 | 9620 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 9621 | |
michael@0 | 9622 | bool resetTimers = (!aIsBackground && IsBackground()); |
michael@0 | 9623 | nsPIDOMWindow::SetIsBackground(aIsBackground); |
michael@0 | 9624 | if (resetTimers) { |
michael@0 | 9625 | ResetTimersForNonBackgroundWindow(); |
michael@0 | 9626 | } |
michael@0 | 9627 | #ifdef MOZ_GAMEPAD |
michael@0 | 9628 | if (!aIsBackground) { |
michael@0 | 9629 | nsGlobalWindow* inner = GetCurrentInnerWindowInternal(); |
michael@0 | 9630 | if (inner) { |
michael@0 | 9631 | inner->SyncGamepadState(); |
michael@0 | 9632 | } |
michael@0 | 9633 | } |
michael@0 | 9634 | #endif |
michael@0 | 9635 | } |
michael@0 | 9636 | |
michael@0 | 9637 | void nsGlobalWindow::MaybeUpdateTouchState() |
michael@0 | 9638 | { |
michael@0 | 9639 | FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ()); |
michael@0 | 9640 | |
michael@0 | 9641 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 9642 | |
michael@0 | 9643 | nsCOMPtr<nsIDOMWindow> focusedWindow; |
michael@0 | 9644 | fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); |
michael@0 | 9645 | |
michael@0 | 9646 | if(this == focusedWindow) { |
michael@0 | 9647 | UpdateTouchState(); |
michael@0 | 9648 | } |
michael@0 | 9649 | |
michael@0 | 9650 | if (mMayHaveTouchEventListener) { |
michael@0 | 9651 | nsCOMPtr<nsIObserverService> observerService = |
michael@0 | 9652 | services::GetObserverService(); |
michael@0 | 9653 | |
michael@0 | 9654 | if (observerService) { |
michael@0 | 9655 | observerService->NotifyObservers(static_cast<nsIDOMWindow*>(this), |
michael@0 | 9656 | DOM_TOUCH_LISTENER_ADDED, |
michael@0 | 9657 | nullptr); |
michael@0 | 9658 | } |
michael@0 | 9659 | } |
michael@0 | 9660 | } |
michael@0 | 9661 | |
michael@0 | 9662 | void nsGlobalWindow::UpdateTouchState() |
michael@0 | 9663 | { |
michael@0 | 9664 | FORWARD_TO_INNER_VOID(UpdateTouchState, ()); |
michael@0 | 9665 | |
michael@0 | 9666 | nsCOMPtr<nsIWidget> mainWidget = GetMainWidget(); |
michael@0 | 9667 | if (!mainWidget) { |
michael@0 | 9668 | return; |
michael@0 | 9669 | } |
michael@0 | 9670 | |
michael@0 | 9671 | if (mMayHaveTouchEventListener) { |
michael@0 | 9672 | mainWidget->RegisterTouchWindow(); |
michael@0 | 9673 | } else { |
michael@0 | 9674 | mainWidget->UnregisterTouchWindow(); |
michael@0 | 9675 | } |
michael@0 | 9676 | } |
michael@0 | 9677 | |
michael@0 | 9678 | void |
michael@0 | 9679 | nsGlobalWindow::EnableGamepadUpdates() |
michael@0 | 9680 | { |
michael@0 | 9681 | FORWARD_TO_INNER_VOID(EnableGamepadUpdates, ()); |
michael@0 | 9682 | if (mHasGamepad) { |
michael@0 | 9683 | #ifdef MOZ_GAMEPAD |
michael@0 | 9684 | nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService()); |
michael@0 | 9685 | if (gamepadsvc) { |
michael@0 | 9686 | gamepadsvc->AddListener(this); |
michael@0 | 9687 | } |
michael@0 | 9688 | #endif |
michael@0 | 9689 | } |
michael@0 | 9690 | } |
michael@0 | 9691 | |
michael@0 | 9692 | void |
michael@0 | 9693 | nsGlobalWindow::DisableGamepadUpdates() |
michael@0 | 9694 | { |
michael@0 | 9695 | FORWARD_TO_INNER_VOID(DisableGamepadUpdates, ()); |
michael@0 | 9696 | if (mHasGamepad) { |
michael@0 | 9697 | #ifdef MOZ_GAMEPAD |
michael@0 | 9698 | nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService()); |
michael@0 | 9699 | if (gamepadsvc) { |
michael@0 | 9700 | gamepadsvc->RemoveListener(this); |
michael@0 | 9701 | } |
michael@0 | 9702 | #endif |
michael@0 | 9703 | } |
michael@0 | 9704 | } |
michael@0 | 9705 | |
michael@0 | 9706 | void |
michael@0 | 9707 | nsGlobalWindow::SetChromeEventHandler(EventTarget* aChromeEventHandler) |
michael@0 | 9708 | { |
michael@0 | 9709 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 9710 | |
michael@0 | 9711 | SetChromeEventHandlerInternal(aChromeEventHandler); |
michael@0 | 9712 | // update the chrome event handler on all our inner windows |
michael@0 | 9713 | for (nsGlobalWindow *inner = (nsGlobalWindow *)PR_LIST_HEAD(this); |
michael@0 | 9714 | inner != this; |
michael@0 | 9715 | inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { |
michael@0 | 9716 | NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, |
michael@0 | 9717 | "bad outer window pointer"); |
michael@0 | 9718 | inner->SetChromeEventHandlerInternal(aChromeEventHandler); |
michael@0 | 9719 | } |
michael@0 | 9720 | } |
michael@0 | 9721 | |
michael@0 | 9722 | static bool IsLink(nsIContent* aContent) |
michael@0 | 9723 | { |
michael@0 | 9724 | return aContent && (aContent->IsHTML(nsGkAtoms::a) || |
michael@0 | 9725 | aContent->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type, |
michael@0 | 9726 | nsGkAtoms::simple, eCaseMatters)); |
michael@0 | 9727 | } |
michael@0 | 9728 | |
michael@0 | 9729 | void |
michael@0 | 9730 | nsGlobalWindow::SetFocusedNode(nsIContent* aNode, |
michael@0 | 9731 | uint32_t aFocusMethod, |
michael@0 | 9732 | bool aNeedsFocus) |
michael@0 | 9733 | { |
michael@0 | 9734 | FORWARD_TO_INNER_VOID(SetFocusedNode, (aNode, aFocusMethod, aNeedsFocus)); |
michael@0 | 9735 | |
michael@0 | 9736 | if (aNode && aNode->GetCurrentDoc() != mDoc) { |
michael@0 | 9737 | NS_WARNING("Trying to set focus to a node from a wrong document"); |
michael@0 | 9738 | return; |
michael@0 | 9739 | } |
michael@0 | 9740 | |
michael@0 | 9741 | if (mCleanedUp) { |
michael@0 | 9742 | NS_ASSERTION(!aNode, "Trying to focus cleaned up window!"); |
michael@0 | 9743 | aNode = nullptr; |
michael@0 | 9744 | aNeedsFocus = false; |
michael@0 | 9745 | } |
michael@0 | 9746 | if (mFocusedNode != aNode) { |
michael@0 | 9747 | UpdateCanvasFocus(false, aNode); |
michael@0 | 9748 | mFocusedNode = aNode; |
michael@0 | 9749 | mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK; |
michael@0 | 9750 | mShowFocusRingForContent = false; |
michael@0 | 9751 | } |
michael@0 | 9752 | |
michael@0 | 9753 | if (mFocusedNode) { |
michael@0 | 9754 | // if a node was focused by a keypress, turn on focus rings for the |
michael@0 | 9755 | // window. |
michael@0 | 9756 | if (mFocusMethod & nsIFocusManager::FLAG_BYKEY) { |
michael@0 | 9757 | mFocusByKeyOccurred = true; |
michael@0 | 9758 | } else if ( |
michael@0 | 9759 | // otherwise, we set mShowFocusRingForContent, as we don't want this to |
michael@0 | 9760 | // be permanent for the window. On Windows, focus rings are only shown |
michael@0 | 9761 | // when the FLAG_SHOWRING flag is used. On other platforms, focus rings |
michael@0 | 9762 | // are only hidden for clicks on links. |
michael@0 | 9763 | #ifndef XP_WIN |
michael@0 | 9764 | !(mFocusMethod & nsIFocusManager::FLAG_BYMOUSE) || !IsLink(aNode) || |
michael@0 | 9765 | #endif |
michael@0 | 9766 | aFocusMethod & nsIFocusManager::FLAG_SHOWRING) { |
michael@0 | 9767 | mShowFocusRingForContent = true; |
michael@0 | 9768 | } |
michael@0 | 9769 | } |
michael@0 | 9770 | |
michael@0 | 9771 | if (aNeedsFocus) |
michael@0 | 9772 | mNeedsFocus = aNeedsFocus; |
michael@0 | 9773 | } |
michael@0 | 9774 | |
michael@0 | 9775 | uint32_t |
michael@0 | 9776 | nsGlobalWindow::GetFocusMethod() |
michael@0 | 9777 | { |
michael@0 | 9778 | FORWARD_TO_INNER(GetFocusMethod, (), 0); |
michael@0 | 9779 | |
michael@0 | 9780 | return mFocusMethod; |
michael@0 | 9781 | } |
michael@0 | 9782 | |
michael@0 | 9783 | bool |
michael@0 | 9784 | nsGlobalWindow::ShouldShowFocusRing() |
michael@0 | 9785 | { |
michael@0 | 9786 | FORWARD_TO_INNER(ShouldShowFocusRing, (), false); |
michael@0 | 9787 | |
michael@0 | 9788 | return mShowFocusRings || mShowFocusRingForContent || mFocusByKeyOccurred; |
michael@0 | 9789 | } |
michael@0 | 9790 | |
michael@0 | 9791 | void |
michael@0 | 9792 | nsGlobalWindow::SetKeyboardIndicators(UIStateChangeType aShowAccelerators, |
michael@0 | 9793 | UIStateChangeType aShowFocusRings) |
michael@0 | 9794 | { |
michael@0 | 9795 | FORWARD_TO_INNER_VOID(SetKeyboardIndicators, (aShowAccelerators, aShowFocusRings)); |
michael@0 | 9796 | |
michael@0 | 9797 | bool oldShouldShowFocusRing = ShouldShowFocusRing(); |
michael@0 | 9798 | |
michael@0 | 9799 | // only change the flags that have been modified |
michael@0 | 9800 | if (aShowAccelerators != UIStateChangeType_NoChange) |
michael@0 | 9801 | mShowAccelerators = aShowAccelerators == UIStateChangeType_Set; |
michael@0 | 9802 | if (aShowFocusRings != UIStateChangeType_NoChange) |
michael@0 | 9803 | mShowFocusRings = aShowFocusRings == UIStateChangeType_Set; |
michael@0 | 9804 | |
michael@0 | 9805 | // propagate the indicators to child windows |
michael@0 | 9806 | nsCOMPtr<nsIDocShell> docShell = GetDocShell(); |
michael@0 | 9807 | if (docShell) { |
michael@0 | 9808 | int32_t childCount = 0; |
michael@0 | 9809 | docShell->GetChildCount(&childCount); |
michael@0 | 9810 | |
michael@0 | 9811 | for (int32_t i = 0; i < childCount; ++i) { |
michael@0 | 9812 | nsCOMPtr<nsIDocShellTreeItem> childShell; |
michael@0 | 9813 | docShell->GetChildAt(i, getter_AddRefs(childShell)); |
michael@0 | 9814 | nsCOMPtr<nsPIDOMWindow> childWindow = do_GetInterface(childShell); |
michael@0 | 9815 | if (childWindow) { |
michael@0 | 9816 | childWindow->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings); |
michael@0 | 9817 | } |
michael@0 | 9818 | } |
michael@0 | 9819 | } |
michael@0 | 9820 | |
michael@0 | 9821 | bool newShouldShowFocusRing = ShouldShowFocusRing(); |
michael@0 | 9822 | if (mHasFocus && mFocusedNode && |
michael@0 | 9823 | oldShouldShowFocusRing != newShouldShowFocusRing && |
michael@0 | 9824 | mFocusedNode->IsElement()) { |
michael@0 | 9825 | // Update mFocusedNode's state. |
michael@0 | 9826 | if (newShouldShowFocusRing) { |
michael@0 | 9827 | mFocusedNode->AsElement()->AddStates(NS_EVENT_STATE_FOCUSRING); |
michael@0 | 9828 | } else { |
michael@0 | 9829 | mFocusedNode->AsElement()->RemoveStates(NS_EVENT_STATE_FOCUSRING); |
michael@0 | 9830 | } |
michael@0 | 9831 | } |
michael@0 | 9832 | } |
michael@0 | 9833 | |
michael@0 | 9834 | void |
michael@0 | 9835 | nsGlobalWindow::GetKeyboardIndicators(bool* aShowAccelerators, |
michael@0 | 9836 | bool* aShowFocusRings) |
michael@0 | 9837 | { |
michael@0 | 9838 | FORWARD_TO_INNER_VOID(GetKeyboardIndicators, (aShowAccelerators, aShowFocusRings)); |
michael@0 | 9839 | |
michael@0 | 9840 | *aShowAccelerators = mShowAccelerators; |
michael@0 | 9841 | *aShowFocusRings = mShowFocusRings; |
michael@0 | 9842 | } |
michael@0 | 9843 | |
michael@0 | 9844 | bool |
michael@0 | 9845 | nsGlobalWindow::TakeFocus(bool aFocus, uint32_t aFocusMethod) |
michael@0 | 9846 | { |
michael@0 | 9847 | FORWARD_TO_INNER(TakeFocus, (aFocus, aFocusMethod), false); |
michael@0 | 9848 | |
michael@0 | 9849 | if (mCleanedUp) { |
michael@0 | 9850 | return false; |
michael@0 | 9851 | } |
michael@0 | 9852 | |
michael@0 | 9853 | if (aFocus) |
michael@0 | 9854 | mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK; |
michael@0 | 9855 | |
michael@0 | 9856 | if (mHasFocus != aFocus) { |
michael@0 | 9857 | mHasFocus = aFocus; |
michael@0 | 9858 | UpdateCanvasFocus(true, mFocusedNode); |
michael@0 | 9859 | } |
michael@0 | 9860 | |
michael@0 | 9861 | // if mNeedsFocus is true, then the document has not yet received a |
michael@0 | 9862 | // document-level focus event. If there is a root content node, then return |
michael@0 | 9863 | // true to tell the calling focus manager that a focus event is expected. If |
michael@0 | 9864 | // there is no root content node, the document hasn't loaded enough yet, or |
michael@0 | 9865 | // there isn't one and there is no point in firing a focus event. |
michael@0 | 9866 | if (aFocus && mNeedsFocus && mDoc && mDoc->GetRootElement() != nullptr) { |
michael@0 | 9867 | mNeedsFocus = false; |
michael@0 | 9868 | return true; |
michael@0 | 9869 | } |
michael@0 | 9870 | |
michael@0 | 9871 | mNeedsFocus = false; |
michael@0 | 9872 | return false; |
michael@0 | 9873 | } |
michael@0 | 9874 | |
michael@0 | 9875 | void |
michael@0 | 9876 | nsGlobalWindow::SetReadyForFocus() |
michael@0 | 9877 | { |
michael@0 | 9878 | FORWARD_TO_INNER_VOID(SetReadyForFocus, ()); |
michael@0 | 9879 | |
michael@0 | 9880 | bool oldNeedsFocus = mNeedsFocus; |
michael@0 | 9881 | mNeedsFocus = false; |
michael@0 | 9882 | |
michael@0 | 9883 | // update whether focus rings need to be shown using the state from the |
michael@0 | 9884 | // root window |
michael@0 | 9885 | nsPIDOMWindow* root = GetPrivateRoot(); |
michael@0 | 9886 | if (root) { |
michael@0 | 9887 | bool showAccelerators, showFocusRings; |
michael@0 | 9888 | root->GetKeyboardIndicators(&showAccelerators, &showFocusRings); |
michael@0 | 9889 | mShowFocusRings = showFocusRings; |
michael@0 | 9890 | } |
michael@0 | 9891 | |
michael@0 | 9892 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 9893 | if (fm) |
michael@0 | 9894 | fm->WindowShown(this, oldNeedsFocus); |
michael@0 | 9895 | } |
michael@0 | 9896 | |
michael@0 | 9897 | void |
michael@0 | 9898 | nsGlobalWindow::PageHidden() |
michael@0 | 9899 | { |
michael@0 | 9900 | FORWARD_TO_INNER_VOID(PageHidden, ()); |
michael@0 | 9901 | |
michael@0 | 9902 | // the window is being hidden, so tell the focus manager that the frame is |
michael@0 | 9903 | // no longer valid. Use the persisted field to determine if the document |
michael@0 | 9904 | // is being destroyed. |
michael@0 | 9905 | |
michael@0 | 9906 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 9907 | if (fm) |
michael@0 | 9908 | fm->WindowHidden(this); |
michael@0 | 9909 | |
michael@0 | 9910 | mNeedsFocus = true; |
michael@0 | 9911 | } |
michael@0 | 9912 | |
michael@0 | 9913 | class HashchangeCallback : public nsRunnable |
michael@0 | 9914 | { |
michael@0 | 9915 | public: |
michael@0 | 9916 | HashchangeCallback(const nsAString &aOldURL, |
michael@0 | 9917 | const nsAString &aNewURL, |
michael@0 | 9918 | nsGlobalWindow* aWindow) |
michael@0 | 9919 | : mWindow(aWindow) |
michael@0 | 9920 | { |
michael@0 | 9921 | MOZ_ASSERT(mWindow); |
michael@0 | 9922 | MOZ_ASSERT(mWindow->IsInnerWindow()); |
michael@0 | 9923 | mOldURL.Assign(aOldURL); |
michael@0 | 9924 | mNewURL.Assign(aNewURL); |
michael@0 | 9925 | } |
michael@0 | 9926 | |
michael@0 | 9927 | NS_IMETHOD Run() |
michael@0 | 9928 | { |
michael@0 | 9929 | NS_PRECONDITION(NS_IsMainThread(), "Should be called on the main thread."); |
michael@0 | 9930 | return mWindow->FireHashchange(mOldURL, mNewURL); |
michael@0 | 9931 | } |
michael@0 | 9932 | |
michael@0 | 9933 | private: |
michael@0 | 9934 | nsString mOldURL; |
michael@0 | 9935 | nsString mNewURL; |
michael@0 | 9936 | nsRefPtr<nsGlobalWindow> mWindow; |
michael@0 | 9937 | }; |
michael@0 | 9938 | |
michael@0 | 9939 | nsresult |
michael@0 | 9940 | nsGlobalWindow::DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI) |
michael@0 | 9941 | { |
michael@0 | 9942 | FORWARD_TO_INNER(DispatchAsyncHashchange, (aOldURI, aNewURI), NS_OK); |
michael@0 | 9943 | |
michael@0 | 9944 | // Make sure that aOldURI and aNewURI are identical up to the '#', and that |
michael@0 | 9945 | // their hashes are different. |
michael@0 | 9946 | nsAutoCString oldBeforeHash, oldHash, newBeforeHash, newHash; |
michael@0 | 9947 | nsContentUtils::SplitURIAtHash(aOldURI, oldBeforeHash, oldHash); |
michael@0 | 9948 | nsContentUtils::SplitURIAtHash(aNewURI, newBeforeHash, newHash); |
michael@0 | 9949 | |
michael@0 | 9950 | NS_ENSURE_STATE(oldBeforeHash.Equals(newBeforeHash)); |
michael@0 | 9951 | NS_ENSURE_STATE(!oldHash.Equals(newHash)); |
michael@0 | 9952 | |
michael@0 | 9953 | nsAutoCString oldSpec, newSpec; |
michael@0 | 9954 | aOldURI->GetSpec(oldSpec); |
michael@0 | 9955 | aNewURI->GetSpec(newSpec); |
michael@0 | 9956 | |
michael@0 | 9957 | NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec); |
michael@0 | 9958 | NS_ConvertUTF8toUTF16 newWideSpec(newSpec); |
michael@0 | 9959 | |
michael@0 | 9960 | nsCOMPtr<nsIRunnable> callback = |
michael@0 | 9961 | new HashchangeCallback(oldWideSpec, newWideSpec, this); |
michael@0 | 9962 | return NS_DispatchToMainThread(callback); |
michael@0 | 9963 | } |
michael@0 | 9964 | |
michael@0 | 9965 | nsresult |
michael@0 | 9966 | nsGlobalWindow::FireHashchange(const nsAString &aOldURL, |
michael@0 | 9967 | const nsAString &aNewURL) |
michael@0 | 9968 | { |
michael@0 | 9969 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 9970 | |
michael@0 | 9971 | // Don't do anything if the window is frozen. |
michael@0 | 9972 | if (IsFrozen()) |
michael@0 | 9973 | return NS_OK; |
michael@0 | 9974 | |
michael@0 | 9975 | // Get a presentation shell for use in creating the hashchange event. |
michael@0 | 9976 | NS_ENSURE_STATE(IsCurrentInnerWindow()); |
michael@0 | 9977 | |
michael@0 | 9978 | nsIPresShell *shell = mDoc->GetShell(); |
michael@0 | 9979 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 9980 | if (shell) { |
michael@0 | 9981 | presContext = shell->GetPresContext(); |
michael@0 | 9982 | } |
michael@0 | 9983 | |
michael@0 | 9984 | // Create a new hashchange event. |
michael@0 | 9985 | nsCOMPtr<nsIDOMEvent> domEvent; |
michael@0 | 9986 | nsresult rv = |
michael@0 | 9987 | EventDispatcher::CreateEvent(this, presContext, nullptr, |
michael@0 | 9988 | NS_LITERAL_STRING("hashchangeevent"), |
michael@0 | 9989 | getter_AddRefs(domEvent)); |
michael@0 | 9990 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 9991 | |
michael@0 | 9992 | nsCOMPtr<nsIDOMHashChangeEvent> hashchangeEvent = do_QueryInterface(domEvent); |
michael@0 | 9993 | NS_ENSURE_TRUE(hashchangeEvent, NS_ERROR_UNEXPECTED); |
michael@0 | 9994 | |
michael@0 | 9995 | // The hashchange event bubbles and isn't cancellable. |
michael@0 | 9996 | rv = hashchangeEvent->InitHashChangeEvent(NS_LITERAL_STRING("hashchange"), |
michael@0 | 9997 | true, false, |
michael@0 | 9998 | aOldURL, aNewURL); |
michael@0 | 9999 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10000 | |
michael@0 | 10001 | domEvent->SetTrusted(true); |
michael@0 | 10002 | |
michael@0 | 10003 | bool dummy; |
michael@0 | 10004 | return DispatchEvent(hashchangeEvent, &dummy); |
michael@0 | 10005 | } |
michael@0 | 10006 | |
michael@0 | 10007 | nsresult |
michael@0 | 10008 | nsGlobalWindow::DispatchSyncPopState() |
michael@0 | 10009 | { |
michael@0 | 10010 | FORWARD_TO_INNER(DispatchSyncPopState, (), NS_OK); |
michael@0 | 10011 | |
michael@0 | 10012 | NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), |
michael@0 | 10013 | "Must be safe to run script here."); |
michael@0 | 10014 | |
michael@0 | 10015 | // Check that PopState hasn't been pref'ed off. |
michael@0 | 10016 | if (!Preferences::GetBool(sPopStatePrefStr, false)) { |
michael@0 | 10017 | return NS_OK; |
michael@0 | 10018 | } |
michael@0 | 10019 | |
michael@0 | 10020 | nsresult rv = NS_OK; |
michael@0 | 10021 | |
michael@0 | 10022 | // Bail if the window is frozen. |
michael@0 | 10023 | if (IsFrozen()) { |
michael@0 | 10024 | return NS_OK; |
michael@0 | 10025 | } |
michael@0 | 10026 | |
michael@0 | 10027 | // Get the document's pending state object -- it contains the data we're |
michael@0 | 10028 | // going to send along with the popstate event. The object is serialized |
michael@0 | 10029 | // using structured clone. |
michael@0 | 10030 | nsCOMPtr<nsIVariant> stateObj; |
michael@0 | 10031 | rv = mDoc->GetStateObject(getter_AddRefs(stateObj)); |
michael@0 | 10032 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10033 | |
michael@0 | 10034 | // Obtain a presentation shell for use in creating a popstate event. |
michael@0 | 10035 | nsIPresShell *shell = mDoc->GetShell(); |
michael@0 | 10036 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 10037 | if (shell) { |
michael@0 | 10038 | presContext = shell->GetPresContext(); |
michael@0 | 10039 | } |
michael@0 | 10040 | |
michael@0 | 10041 | // Create a new popstate event |
michael@0 | 10042 | nsCOMPtr<nsIDOMEvent> domEvent; |
michael@0 | 10043 | rv = EventDispatcher::CreateEvent(this, presContext, nullptr, |
michael@0 | 10044 | NS_LITERAL_STRING("popstateevent"), |
michael@0 | 10045 | getter_AddRefs(domEvent)); |
michael@0 | 10046 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10047 | |
michael@0 | 10048 | // Initialize the popstate event, which does bubble but isn't cancellable. |
michael@0 | 10049 | nsCOMPtr<nsIDOMPopStateEvent> popstateEvent = do_QueryInterface(domEvent); |
michael@0 | 10050 | rv = popstateEvent->InitPopStateEvent(NS_LITERAL_STRING("popstate"), |
michael@0 | 10051 | true, false, |
michael@0 | 10052 | stateObj); |
michael@0 | 10053 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10054 | |
michael@0 | 10055 | domEvent->SetTrusted(true); |
michael@0 | 10056 | |
michael@0 | 10057 | nsCOMPtr<EventTarget> outerWindow = |
michael@0 | 10058 | do_QueryInterface(GetOuterWindow()); |
michael@0 | 10059 | NS_ENSURE_TRUE(outerWindow, NS_ERROR_UNEXPECTED); |
michael@0 | 10060 | |
michael@0 | 10061 | rv = domEvent->SetTarget(outerWindow); |
michael@0 | 10062 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10063 | |
michael@0 | 10064 | bool dummy; // default action |
michael@0 | 10065 | return DispatchEvent(popstateEvent, &dummy); |
michael@0 | 10066 | } |
michael@0 | 10067 | |
michael@0 | 10068 | // Find an nsICanvasFrame under aFrame. Only search the principal |
michael@0 | 10069 | // child lists. aFrame must be non-null. |
michael@0 | 10070 | static nsCanvasFrame* FindCanvasFrame(nsIFrame* aFrame) |
michael@0 | 10071 | { |
michael@0 | 10072 | nsCanvasFrame* canvasFrame = do_QueryFrame(aFrame); |
michael@0 | 10073 | if (canvasFrame) { |
michael@0 | 10074 | return canvasFrame; |
michael@0 | 10075 | } |
michael@0 | 10076 | |
michael@0 | 10077 | nsIFrame* kid = aFrame->GetFirstPrincipalChild(); |
michael@0 | 10078 | while (kid) { |
michael@0 | 10079 | canvasFrame = FindCanvasFrame(kid); |
michael@0 | 10080 | if (canvasFrame) { |
michael@0 | 10081 | return canvasFrame; |
michael@0 | 10082 | } |
michael@0 | 10083 | kid = kid->GetNextSibling(); |
michael@0 | 10084 | } |
michael@0 | 10085 | |
michael@0 | 10086 | return nullptr; |
michael@0 | 10087 | } |
michael@0 | 10088 | |
michael@0 | 10089 | //------------------------------------------------------- |
michael@0 | 10090 | // Tells the HTMLFrame/CanvasFrame that is now has focus |
michael@0 | 10091 | void |
michael@0 | 10092 | nsGlobalWindow::UpdateCanvasFocus(bool aFocusChanged, nsIContent* aNewContent) |
michael@0 | 10093 | { |
michael@0 | 10094 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 10095 | |
michael@0 | 10096 | // this is called from the inner window so use GetDocShell |
michael@0 | 10097 | nsIDocShell* docShell = GetDocShell(); |
michael@0 | 10098 | if (!docShell) |
michael@0 | 10099 | return; |
michael@0 | 10100 | |
michael@0 | 10101 | bool editable; |
michael@0 | 10102 | docShell->GetEditable(&editable); |
michael@0 | 10103 | if (editable) |
michael@0 | 10104 | return; |
michael@0 | 10105 | |
michael@0 | 10106 | nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); |
michael@0 | 10107 | if (!presShell || !mDoc) |
michael@0 | 10108 | return; |
michael@0 | 10109 | |
michael@0 | 10110 | Element *rootElement = mDoc->GetRootElement(); |
michael@0 | 10111 | if (rootElement) { |
michael@0 | 10112 | if ((mHasFocus || aFocusChanged) && |
michael@0 | 10113 | (mFocusedNode == rootElement || aNewContent == rootElement)) { |
michael@0 | 10114 | nsIFrame* frame = rootElement->GetPrimaryFrame(); |
michael@0 | 10115 | if (frame) { |
michael@0 | 10116 | frame = frame->GetParent(); |
michael@0 | 10117 | nsCanvasFrame* canvasFrame = do_QueryFrame(frame); |
michael@0 | 10118 | if (canvasFrame) { |
michael@0 | 10119 | canvasFrame->SetHasFocus(mHasFocus && rootElement == aNewContent); |
michael@0 | 10120 | } |
michael@0 | 10121 | } |
michael@0 | 10122 | } |
michael@0 | 10123 | } else { |
michael@0 | 10124 | // Look for the frame the hard way |
michael@0 | 10125 | nsIFrame* frame = presShell->GetRootFrame(); |
michael@0 | 10126 | if (frame) { |
michael@0 | 10127 | nsCanvasFrame* canvasFrame = FindCanvasFrame(frame); |
michael@0 | 10128 | if (canvasFrame) { |
michael@0 | 10129 | canvasFrame->SetHasFocus(false); |
michael@0 | 10130 | } |
michael@0 | 10131 | } |
michael@0 | 10132 | } |
michael@0 | 10133 | } |
michael@0 | 10134 | |
michael@0 | 10135 | already_AddRefed<nsICSSDeclaration> |
michael@0 | 10136 | nsGlobalWindow::GetComputedStyle(Element& aElt, const nsAString& aPseudoElt, |
michael@0 | 10137 | ErrorResult& aError) |
michael@0 | 10138 | { |
michael@0 | 10139 | return GetComputedStyleHelper(aElt, aPseudoElt, false, aError); |
michael@0 | 10140 | } |
michael@0 | 10141 | |
michael@0 | 10142 | NS_IMETHODIMP |
michael@0 | 10143 | nsGlobalWindow::GetComputedStyle(nsIDOMElement* aElt, |
michael@0 | 10144 | const nsAString& aPseudoElt, |
michael@0 | 10145 | nsIDOMCSSStyleDeclaration** aReturn) |
michael@0 | 10146 | { |
michael@0 | 10147 | return GetComputedStyleHelper(aElt, aPseudoElt, false, aReturn); |
michael@0 | 10148 | } |
michael@0 | 10149 | |
michael@0 | 10150 | already_AddRefed<nsICSSDeclaration> |
michael@0 | 10151 | nsGlobalWindow::GetDefaultComputedStyle(Element& aElt, |
michael@0 | 10152 | const nsAString& aPseudoElt, |
michael@0 | 10153 | ErrorResult& aError) |
michael@0 | 10154 | { |
michael@0 | 10155 | return GetComputedStyleHelper(aElt, aPseudoElt, true, aError); |
michael@0 | 10156 | } |
michael@0 | 10157 | |
michael@0 | 10158 | NS_IMETHODIMP |
michael@0 | 10159 | nsGlobalWindow::GetDefaultComputedStyle(nsIDOMElement* aElt, |
michael@0 | 10160 | const nsAString& aPseudoElt, |
michael@0 | 10161 | nsIDOMCSSStyleDeclaration** aReturn) |
michael@0 | 10162 | { |
michael@0 | 10163 | return GetComputedStyleHelper(aElt, aPseudoElt, true, aReturn); |
michael@0 | 10164 | } |
michael@0 | 10165 | |
michael@0 | 10166 | nsresult |
michael@0 | 10167 | nsGlobalWindow::GetComputedStyleHelper(nsIDOMElement* aElt, |
michael@0 | 10168 | const nsAString& aPseudoElt, |
michael@0 | 10169 | bool aDefaultStylesOnly, |
michael@0 | 10170 | nsIDOMCSSStyleDeclaration** aReturn) |
michael@0 | 10171 | { |
michael@0 | 10172 | NS_ENSURE_ARG_POINTER(aReturn); |
michael@0 | 10173 | *aReturn = nullptr; |
michael@0 | 10174 | |
michael@0 | 10175 | nsCOMPtr<dom::Element> element = do_QueryInterface(aElt); |
michael@0 | 10176 | if (!element) { |
michael@0 | 10177 | return NS_ERROR_DOM_NOT_SUPPORTED_ERR; |
michael@0 | 10178 | } |
michael@0 | 10179 | |
michael@0 | 10180 | ErrorResult rv; |
michael@0 | 10181 | nsCOMPtr<nsIDOMCSSStyleDeclaration> declaration = |
michael@0 | 10182 | GetComputedStyleHelper(*element, aPseudoElt, aDefaultStylesOnly, rv); |
michael@0 | 10183 | declaration.forget(aReturn); |
michael@0 | 10184 | |
michael@0 | 10185 | return rv.ErrorCode(); |
michael@0 | 10186 | } |
michael@0 | 10187 | |
michael@0 | 10188 | already_AddRefed<nsICSSDeclaration> |
michael@0 | 10189 | nsGlobalWindow::GetComputedStyleHelper(Element& aElt, |
michael@0 | 10190 | const nsAString& aPseudoElt, |
michael@0 | 10191 | bool aDefaultStylesOnly, |
michael@0 | 10192 | ErrorResult& aError) |
michael@0 | 10193 | { |
michael@0 | 10194 | FORWARD_TO_OUTER_OR_THROW(GetComputedStyleHelper, |
michael@0 | 10195 | (aElt, aPseudoElt, aDefaultStylesOnly, aError), |
michael@0 | 10196 | aError, nullptr); |
michael@0 | 10197 | |
michael@0 | 10198 | if (!mDocShell) { |
michael@0 | 10199 | return nullptr; |
michael@0 | 10200 | } |
michael@0 | 10201 | |
michael@0 | 10202 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 10203 | |
michael@0 | 10204 | if (!presShell) { |
michael@0 | 10205 | // Try flushing frames on our parent in case there's a pending |
michael@0 | 10206 | // style change that will create the presshell. |
michael@0 | 10207 | nsGlobalWindow *parent = |
michael@0 | 10208 | static_cast<nsGlobalWindow *>(GetPrivateParent()); |
michael@0 | 10209 | if (!parent) { |
michael@0 | 10210 | return nullptr; |
michael@0 | 10211 | } |
michael@0 | 10212 | |
michael@0 | 10213 | parent->FlushPendingNotifications(Flush_Frames); |
michael@0 | 10214 | |
michael@0 | 10215 | // Might have killed mDocShell |
michael@0 | 10216 | if (!mDocShell) { |
michael@0 | 10217 | return nullptr; |
michael@0 | 10218 | } |
michael@0 | 10219 | |
michael@0 | 10220 | presShell = mDocShell->GetPresShell(); |
michael@0 | 10221 | if (!presShell) { |
michael@0 | 10222 | return nullptr; |
michael@0 | 10223 | } |
michael@0 | 10224 | } |
michael@0 | 10225 | |
michael@0 | 10226 | nsRefPtr<nsComputedDOMStyle> compStyle = |
michael@0 | 10227 | NS_NewComputedDOMStyle(&aElt, aPseudoElt, presShell, |
michael@0 | 10228 | aDefaultStylesOnly ? nsComputedDOMStyle::eDefaultOnly : |
michael@0 | 10229 | nsComputedDOMStyle::eAll); |
michael@0 | 10230 | |
michael@0 | 10231 | return compStyle.forget(); |
michael@0 | 10232 | } |
michael@0 | 10233 | |
michael@0 | 10234 | nsIDOMStorage* |
michael@0 | 10235 | nsGlobalWindow::GetSessionStorage(ErrorResult& aError) |
michael@0 | 10236 | { |
michael@0 | 10237 | FORWARD_TO_INNER_OR_THROW(GetSessionStorage, (aError), aError, nullptr); |
michael@0 | 10238 | |
michael@0 | 10239 | nsIPrincipal *principal = GetPrincipal(); |
michael@0 | 10240 | nsIDocShell* docShell = GetDocShell(); |
michael@0 | 10241 | |
michael@0 | 10242 | if (!principal || !docShell || !Preferences::GetBool(kStorageEnabled)) { |
michael@0 | 10243 | return nullptr; |
michael@0 | 10244 | } |
michael@0 | 10245 | |
michael@0 | 10246 | if (mSessionStorage) { |
michael@0 | 10247 | #ifdef PR_LOGGING |
michael@0 | 10248 | if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 10249 | PR_LogPrint("nsGlobalWindow %p has %p sessionStorage", this, mSessionStorage.get()); |
michael@0 | 10250 | } |
michael@0 | 10251 | #endif |
michael@0 | 10252 | nsCOMPtr<nsPIDOMStorage> piStorage = do_QueryInterface(mSessionStorage); |
michael@0 | 10253 | if (piStorage) { |
michael@0 | 10254 | bool canAccess = piStorage->CanAccess(principal); |
michael@0 | 10255 | NS_ASSERTION(canAccess, |
michael@0 | 10256 | "window %x owned sessionStorage " |
michael@0 | 10257 | "that could not be accessed!"); |
michael@0 | 10258 | if (!canAccess) { |
michael@0 | 10259 | mSessionStorage = nullptr; |
michael@0 | 10260 | } |
michael@0 | 10261 | } |
michael@0 | 10262 | } |
michael@0 | 10263 | |
michael@0 | 10264 | if (!mSessionStorage) { |
michael@0 | 10265 | nsString documentURI; |
michael@0 | 10266 | if (mDoc) { |
michael@0 | 10267 | mDoc->GetDocumentURI(documentURI); |
michael@0 | 10268 | } |
michael@0 | 10269 | |
michael@0 | 10270 | // If the document has the sandboxed origin flag set |
michael@0 | 10271 | // don't allow access to sessionStorage. |
michael@0 | 10272 | if (!mDoc) { |
michael@0 | 10273 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 10274 | return nullptr; |
michael@0 | 10275 | } |
michael@0 | 10276 | |
michael@0 | 10277 | if (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) { |
michael@0 | 10278 | aError.Throw(NS_ERROR_DOM_SECURITY_ERR); |
michael@0 | 10279 | return nullptr; |
michael@0 | 10280 | } |
michael@0 | 10281 | |
michael@0 | 10282 | nsresult rv; |
michael@0 | 10283 | |
michael@0 | 10284 | nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv); |
michael@0 | 10285 | if (NS_FAILED(rv)) { |
michael@0 | 10286 | aError.Throw(rv); |
michael@0 | 10287 | return nullptr; |
michael@0 | 10288 | } |
michael@0 | 10289 | |
michael@0 | 10290 | nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); |
michael@0 | 10291 | |
michael@0 | 10292 | nsCOMPtr<nsIURI> firstPartyIsolationURI; |
michael@0 | 10293 | rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI)); |
michael@0 | 10294 | if (NS_FAILED(rv)) { |
michael@0 | 10295 | aError.Throw(rv); |
michael@0 | 10296 | return nullptr; |
michael@0 | 10297 | } |
michael@0 | 10298 | |
michael@0 | 10299 | aError = storageManager->CreateStorageForFirstParty(firstPartyIsolationURI, principal, |
michael@0 | 10300 | documentURI, |
michael@0 | 10301 | loadContext && loadContext->UsePrivateBrowsing(), |
michael@0 | 10302 | getter_AddRefs(mSessionStorage)); |
michael@0 | 10303 | if (aError.Failed()) { |
michael@0 | 10304 | return nullptr; |
michael@0 | 10305 | } |
michael@0 | 10306 | |
michael@0 | 10307 | #ifdef PR_LOGGING |
michael@0 | 10308 | if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 10309 | PR_LogPrint("nsGlobalWindow %p tried to get a new sessionStorage %p", this, mSessionStorage.get()); |
michael@0 | 10310 | } |
michael@0 | 10311 | #endif |
michael@0 | 10312 | |
michael@0 | 10313 | if (!mSessionStorage) { |
michael@0 | 10314 | aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
michael@0 | 10315 | return nullptr; |
michael@0 | 10316 | } |
michael@0 | 10317 | } |
michael@0 | 10318 | |
michael@0 | 10319 | #ifdef PR_LOGGING |
michael@0 | 10320 | if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 10321 | PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get()); |
michael@0 | 10322 | } |
michael@0 | 10323 | #endif |
michael@0 | 10324 | |
michael@0 | 10325 | return mSessionStorage; |
michael@0 | 10326 | } |
michael@0 | 10327 | |
michael@0 | 10328 | NS_IMETHODIMP |
michael@0 | 10329 | nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage) |
michael@0 | 10330 | { |
michael@0 | 10331 | ErrorResult rv; |
michael@0 | 10332 | nsCOMPtr<nsIDOMStorage> storage = GetSessionStorage(rv); |
michael@0 | 10333 | storage.forget(aSessionStorage); |
michael@0 | 10334 | |
michael@0 | 10335 | return rv.ErrorCode(); |
michael@0 | 10336 | } |
michael@0 | 10337 | |
michael@0 | 10338 | nsIDOMStorage* |
michael@0 | 10339 | nsGlobalWindow::GetLocalStorage(ErrorResult& aError) |
michael@0 | 10340 | { |
michael@0 | 10341 | FORWARD_TO_INNER_OR_THROW(GetLocalStorage, (aError), aError, nullptr); |
michael@0 | 10342 | |
michael@0 | 10343 | if (!Preferences::GetBool(kStorageEnabled)) { |
michael@0 | 10344 | return nullptr; |
michael@0 | 10345 | } |
michael@0 | 10346 | |
michael@0 | 10347 | if (!mLocalStorage) { |
michael@0 | 10348 | if (!DOMStorage::CanUseStorage()) { |
michael@0 | 10349 | aError.Throw(NS_ERROR_DOM_SECURITY_ERR); |
michael@0 | 10350 | return nullptr; |
michael@0 | 10351 | } |
michael@0 | 10352 | |
michael@0 | 10353 | nsIPrincipal *principal = GetPrincipal(); |
michael@0 | 10354 | if (!principal) { |
michael@0 | 10355 | return nullptr; |
michael@0 | 10356 | } |
michael@0 | 10357 | |
michael@0 | 10358 | nsresult rv; |
michael@0 | 10359 | nsCOMPtr<nsIDOMStorageManager> storageManager = |
michael@0 | 10360 | do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); |
michael@0 | 10361 | if (NS_FAILED(rv)) { |
michael@0 | 10362 | aError.Throw(rv); |
michael@0 | 10363 | return nullptr; |
michael@0 | 10364 | } |
michael@0 | 10365 | |
michael@0 | 10366 | // If the document has the sandboxed origin flag set |
michael@0 | 10367 | // don't allow access to localStorage. |
michael@0 | 10368 | if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) { |
michael@0 | 10369 | aError.Throw(NS_ERROR_DOM_SECURITY_ERR); |
michael@0 | 10370 | return nullptr; |
michael@0 | 10371 | } |
michael@0 | 10372 | |
michael@0 | 10373 | nsString documentURI; |
michael@0 | 10374 | if (mDoc) { |
michael@0 | 10375 | mDoc->GetDocumentURI(documentURI); |
michael@0 | 10376 | } |
michael@0 | 10377 | |
michael@0 | 10378 | nsCOMPtr<nsIURI> firstPartyIsolationURI; |
michael@0 | 10379 | rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI)); |
michael@0 | 10380 | if (NS_FAILED(rv)) { |
michael@0 | 10381 | aError.Throw(rv); |
michael@0 | 10382 | return nullptr; |
michael@0 | 10383 | } |
michael@0 | 10384 | |
michael@0 | 10385 | nsIDocShell* docShell = GetDocShell(); |
michael@0 | 10386 | nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); |
michael@0 | 10387 | |
michael@0 | 10388 | aError = storageManager->CreateStorageForFirstParty(firstPartyIsolationURI, principal, |
michael@0 | 10389 | documentURI, |
michael@0 | 10390 | loadContext && loadContext->UsePrivateBrowsing(), |
michael@0 | 10391 | getter_AddRefs(mLocalStorage)); |
michael@0 | 10392 | } |
michael@0 | 10393 | |
michael@0 | 10394 | return mLocalStorage; |
michael@0 | 10395 | } |
michael@0 | 10396 | |
michael@0 | 10397 | NS_IMETHODIMP |
michael@0 | 10398 | nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage) |
michael@0 | 10399 | { |
michael@0 | 10400 | NS_ENSURE_ARG(aLocalStorage); |
michael@0 | 10401 | |
michael@0 | 10402 | ErrorResult rv; |
michael@0 | 10403 | nsCOMPtr<nsIDOMStorage> storage = GetLocalStorage(rv); |
michael@0 | 10404 | storage.forget(aLocalStorage); |
michael@0 | 10405 | |
michael@0 | 10406 | return rv.ErrorCode(); |
michael@0 | 10407 | } |
michael@0 | 10408 | |
michael@0 | 10409 | indexedDB::IDBFactory* |
michael@0 | 10410 | nsGlobalWindow::GetIndexedDB(ErrorResult& aError) |
michael@0 | 10411 | { |
michael@0 | 10412 | if (!mIndexedDB) { |
michael@0 | 10413 | // If the document has the sandboxed origin flag set |
michael@0 | 10414 | // don't allow access to indexedDB. |
michael@0 | 10415 | if (mDoc && (mDoc->GetSandboxFlags() & SANDBOXED_ORIGIN)) { |
michael@0 | 10416 | aError.Throw(NS_ERROR_DOM_SECURITY_ERR); |
michael@0 | 10417 | return nullptr; |
michael@0 | 10418 | } |
michael@0 | 10419 | |
michael@0 | 10420 | if (!IsChromeWindow()) { |
michael@0 | 10421 | // Whitelist about:home, since it doesn't have a base domain it would not |
michael@0 | 10422 | // pass the thirdPartyUtil check, though it should be able to use |
michael@0 | 10423 | // indexedDB. |
michael@0 | 10424 | bool skipThirdPartyCheck = false; |
michael@0 | 10425 | nsIPrincipal *principal = GetPrincipal(); |
michael@0 | 10426 | if (principal) { |
michael@0 | 10427 | nsCOMPtr<nsIURI> uri; |
michael@0 | 10428 | principal->GetURI(getter_AddRefs(uri)); |
michael@0 | 10429 | bool isAbout = false; |
michael@0 | 10430 | if (uri && NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) { |
michael@0 | 10431 | nsAutoCString path; |
michael@0 | 10432 | skipThirdPartyCheck = NS_SUCCEEDED(uri->GetPath(path)) && |
michael@0 | 10433 | path.EqualsLiteral("home"); |
michael@0 | 10434 | } |
michael@0 | 10435 | } |
michael@0 | 10436 | |
michael@0 | 10437 | if (!skipThirdPartyCheck) { |
michael@0 | 10438 | nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = |
michael@0 | 10439 | do_GetService(THIRDPARTYUTIL_CONTRACTID); |
michael@0 | 10440 | if (!thirdPartyUtil) { |
michael@0 | 10441 | aError.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
michael@0 | 10442 | return nullptr; |
michael@0 | 10443 | } |
michael@0 | 10444 | |
michael@0 | 10445 | bool isThirdParty; |
michael@0 | 10446 | aError = thirdPartyUtil->IsThirdPartyWindow(this, nullptr, |
michael@0 | 10447 | &isThirdParty); |
michael@0 | 10448 | if (aError.Failed() || isThirdParty) { |
michael@0 | 10449 | NS_WARN_IF_FALSE(aError.Failed(), |
michael@0 | 10450 | "IndexedDB is not permitted in a third-party window."); |
michael@0 | 10451 | return nullptr; |
michael@0 | 10452 | } |
michael@0 | 10453 | } |
michael@0 | 10454 | } |
michael@0 | 10455 | |
michael@0 | 10456 | // This may be null if being created from a file. |
michael@0 | 10457 | aError = indexedDB::IDBFactory::Create(this, nullptr, |
michael@0 | 10458 | getter_AddRefs(mIndexedDB)); |
michael@0 | 10459 | } |
michael@0 | 10460 | |
michael@0 | 10461 | return mIndexedDB; |
michael@0 | 10462 | } |
michael@0 | 10463 | |
michael@0 | 10464 | NS_IMETHODIMP |
michael@0 | 10465 | nsGlobalWindow::GetIndexedDB(nsISupports** _retval) |
michael@0 | 10466 | { |
michael@0 | 10467 | ErrorResult rv; |
michael@0 | 10468 | nsCOMPtr<nsISupports> request(GetIndexedDB(rv)); |
michael@0 | 10469 | request.forget(_retval); |
michael@0 | 10470 | |
michael@0 | 10471 | return rv.ErrorCode(); |
michael@0 | 10472 | } |
michael@0 | 10473 | |
michael@0 | 10474 | NS_IMETHODIMP |
michael@0 | 10475 | nsGlobalWindow::GetMozIndexedDB(nsISupports** _retval) |
michael@0 | 10476 | { |
michael@0 | 10477 | return GetIndexedDB(_retval); |
michael@0 | 10478 | } |
michael@0 | 10479 | |
michael@0 | 10480 | //***************************************************************************** |
michael@0 | 10481 | // nsGlobalWindow::nsIInterfaceRequestor |
michael@0 | 10482 | //***************************************************************************** |
michael@0 | 10483 | |
michael@0 | 10484 | NS_IMETHODIMP |
michael@0 | 10485 | nsGlobalWindow::GetInterface(const nsIID & aIID, void **aSink) |
michael@0 | 10486 | { |
michael@0 | 10487 | NS_ENSURE_ARG_POINTER(aSink); |
michael@0 | 10488 | *aSink = nullptr; |
michael@0 | 10489 | |
michael@0 | 10490 | if (aIID.Equals(NS_GET_IID(nsIDocCharset))) { |
michael@0 | 10491 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 10492 | NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 10493 | |
michael@0 | 10494 | NS_WARNING("Using deprecated nsIDocCharset: use nsIDocShell.GetCharset() instead "); |
michael@0 | 10495 | nsCOMPtr<nsIDocCharset> docCharset(do_QueryInterface(outer->mDocShell)); |
michael@0 | 10496 | docCharset.forget(aSink); |
michael@0 | 10497 | } |
michael@0 | 10498 | else if (aIID.Equals(NS_GET_IID(nsIWebNavigation))) { |
michael@0 | 10499 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 10500 | NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 10501 | |
michael@0 | 10502 | nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(outer->mDocShell)); |
michael@0 | 10503 | webNav.forget(aSink); |
michael@0 | 10504 | } |
michael@0 | 10505 | else if (aIID.Equals(NS_GET_IID(nsIDocShell))) { |
michael@0 | 10506 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 10507 | NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 10508 | |
michael@0 | 10509 | nsCOMPtr<nsIDocShell> docShell = outer->mDocShell; |
michael@0 | 10510 | docShell.forget(aSink); |
michael@0 | 10511 | } |
michael@0 | 10512 | #ifdef NS_PRINTING |
michael@0 | 10513 | else if (aIID.Equals(NS_GET_IID(nsIWebBrowserPrint))) { |
michael@0 | 10514 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 10515 | NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 10516 | |
michael@0 | 10517 | if (outer->mDocShell) { |
michael@0 | 10518 | nsCOMPtr<nsIContentViewer> viewer; |
michael@0 | 10519 | outer->mDocShell->GetContentViewer(getter_AddRefs(viewer)); |
michael@0 | 10520 | if (viewer) { |
michael@0 | 10521 | nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint(do_QueryInterface(viewer)); |
michael@0 | 10522 | webBrowserPrint.forget(aSink); |
michael@0 | 10523 | } |
michael@0 | 10524 | } |
michael@0 | 10525 | } |
michael@0 | 10526 | #endif |
michael@0 | 10527 | else if (aIID.Equals(NS_GET_IID(nsIDOMWindowUtils))) { |
michael@0 | 10528 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 10529 | NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 10530 | |
michael@0 | 10531 | if (!mWindowUtils) { |
michael@0 | 10532 | mWindowUtils = new nsDOMWindowUtils(outer); |
michael@0 | 10533 | } |
michael@0 | 10534 | |
michael@0 | 10535 | *aSink = mWindowUtils; |
michael@0 | 10536 | NS_ADDREF(((nsISupports *) *aSink)); |
michael@0 | 10537 | } |
michael@0 | 10538 | else if (aIID.Equals(NS_GET_IID(nsILoadContext))) { |
michael@0 | 10539 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 10540 | NS_ENSURE_TRUE(outer, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 10541 | |
michael@0 | 10542 | nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(outer->mDocShell)); |
michael@0 | 10543 | loadContext.forget(aSink); |
michael@0 | 10544 | } |
michael@0 | 10545 | else { |
michael@0 | 10546 | return QueryInterface(aIID, aSink); |
michael@0 | 10547 | } |
michael@0 | 10548 | |
michael@0 | 10549 | return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE; |
michael@0 | 10550 | } |
michael@0 | 10551 | |
michael@0 | 10552 | void |
michael@0 | 10553 | nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID, |
michael@0 | 10554 | JS::MutableHandle<JS::Value> aRetval, |
michael@0 | 10555 | ErrorResult& aError) |
michael@0 | 10556 | { |
michael@0 | 10557 | dom::GetInterface(aCx, this, aIID, aRetval, aError); |
michael@0 | 10558 | } |
michael@0 | 10559 | |
michael@0 | 10560 | void |
michael@0 | 10561 | nsGlobalWindow::FireOfflineStatusEvent() |
michael@0 | 10562 | { |
michael@0 | 10563 | if (!IsCurrentInnerWindow()) |
michael@0 | 10564 | return; |
michael@0 | 10565 | nsAutoString name; |
michael@0 | 10566 | if (NS_IsOffline()) { |
michael@0 | 10567 | name.AssignLiteral("offline"); |
michael@0 | 10568 | } else { |
michael@0 | 10569 | name.AssignLiteral("online"); |
michael@0 | 10570 | } |
michael@0 | 10571 | // The event is fired at the body element, or if there is no body element, |
michael@0 | 10572 | // at the document. |
michael@0 | 10573 | nsCOMPtr<EventTarget> eventTarget = mDoc.get(); |
michael@0 | 10574 | nsHTMLDocument* htmlDoc = mDoc->AsHTMLDocument(); |
michael@0 | 10575 | if (htmlDoc) { |
michael@0 | 10576 | Element* body = htmlDoc->GetBody(); |
michael@0 | 10577 | if (body) { |
michael@0 | 10578 | eventTarget = body; |
michael@0 | 10579 | } |
michael@0 | 10580 | } else { |
michael@0 | 10581 | Element* documentElement = mDoc->GetDocumentElement(); |
michael@0 | 10582 | if (documentElement) { |
michael@0 | 10583 | eventTarget = documentElement; |
michael@0 | 10584 | } |
michael@0 | 10585 | } |
michael@0 | 10586 | nsContentUtils::DispatchTrustedEvent(mDoc, eventTarget, name, true, false); |
michael@0 | 10587 | } |
michael@0 | 10588 | |
michael@0 | 10589 | class NotifyIdleObserverRunnable : public nsRunnable |
michael@0 | 10590 | { |
michael@0 | 10591 | public: |
michael@0 | 10592 | NotifyIdleObserverRunnable(nsIIdleObserver* aIdleObserver, |
michael@0 | 10593 | uint32_t aTimeInS, |
michael@0 | 10594 | bool aCallOnidle, |
michael@0 | 10595 | nsGlobalWindow* aIdleWindow) |
michael@0 | 10596 | : mIdleObserver(aIdleObserver), mTimeInS(aTimeInS), mIdleWindow(aIdleWindow), |
michael@0 | 10597 | mCallOnidle(aCallOnidle) |
michael@0 | 10598 | { } |
michael@0 | 10599 | |
michael@0 | 10600 | NS_IMETHOD Run() |
michael@0 | 10601 | { |
michael@0 | 10602 | if (mIdleWindow->ContainsIdleObserver(mIdleObserver, mTimeInS)) { |
michael@0 | 10603 | return mCallOnidle ? mIdleObserver->Onidle() : mIdleObserver->Onactive(); |
michael@0 | 10604 | } |
michael@0 | 10605 | return NS_OK; |
michael@0 | 10606 | } |
michael@0 | 10607 | |
michael@0 | 10608 | private: |
michael@0 | 10609 | nsCOMPtr<nsIIdleObserver> mIdleObserver; |
michael@0 | 10610 | uint32_t mTimeInS; |
michael@0 | 10611 | nsRefPtr<nsGlobalWindow> mIdleWindow; |
michael@0 | 10612 | |
michael@0 | 10613 | // If false then call on active |
michael@0 | 10614 | bool mCallOnidle; |
michael@0 | 10615 | }; |
michael@0 | 10616 | |
michael@0 | 10617 | void |
michael@0 | 10618 | nsGlobalWindow::NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder, |
michael@0 | 10619 | bool aCallOnidle) |
michael@0 | 10620 | { |
michael@0 | 10621 | MOZ_ASSERT(aIdleObserverHolder); |
michael@0 | 10622 | aIdleObserverHolder->mPrevNotificationIdle = aCallOnidle; |
michael@0 | 10623 | |
michael@0 | 10624 | nsCOMPtr<nsIRunnable> caller = |
michael@0 | 10625 | new NotifyIdleObserverRunnable(aIdleObserverHolder->mIdleObserver, |
michael@0 | 10626 | aIdleObserverHolder->mTimeInS, |
michael@0 | 10627 | aCallOnidle, this); |
michael@0 | 10628 | if (NS_FAILED(NS_DispatchToCurrentThread(caller))) { |
michael@0 | 10629 | NS_WARNING("Failed to dispatch thread for idle observer notification."); |
michael@0 | 10630 | } |
michael@0 | 10631 | } |
michael@0 | 10632 | |
michael@0 | 10633 | bool |
michael@0 | 10634 | nsGlobalWindow::ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t aTimeInS) |
michael@0 | 10635 | { |
michael@0 | 10636 | MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated."); |
michael@0 | 10637 | bool found = false; |
michael@0 | 10638 | nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers); |
michael@0 | 10639 | while (iter.HasMore()) { |
michael@0 | 10640 | IdleObserverHolder& idleObserver = iter.GetNext(); |
michael@0 | 10641 | if (idleObserver.mIdleObserver == aIdleObserver && |
michael@0 | 10642 | idleObserver.mTimeInS == aTimeInS) { |
michael@0 | 10643 | found = true; |
michael@0 | 10644 | break; |
michael@0 | 10645 | } |
michael@0 | 10646 | } |
michael@0 | 10647 | return found; |
michael@0 | 10648 | } |
michael@0 | 10649 | |
michael@0 | 10650 | void |
michael@0 | 10651 | IdleActiveTimerCallback(nsITimer* aTimer, void* aClosure) |
michael@0 | 10652 | { |
michael@0 | 10653 | nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure); |
michael@0 | 10654 | MOZ_ASSERT(idleWindow, "Idle window has not been instantiated."); |
michael@0 | 10655 | idleWindow->HandleIdleActiveEvent(); |
michael@0 | 10656 | } |
michael@0 | 10657 | |
michael@0 | 10658 | void |
michael@0 | 10659 | IdleObserverTimerCallback(nsITimer* aTimer, void* aClosure) |
michael@0 | 10660 | { |
michael@0 | 10661 | nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure); |
michael@0 | 10662 | MOZ_ASSERT(idleWindow, "Idle window has not been instantiated."); |
michael@0 | 10663 | idleWindow->HandleIdleObserverCallback(); |
michael@0 | 10664 | } |
michael@0 | 10665 | |
michael@0 | 10666 | void |
michael@0 | 10667 | nsGlobalWindow::HandleIdleObserverCallback() |
michael@0 | 10668 | { |
michael@0 | 10669 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 10670 | MOZ_ASSERT(static_cast<uint32_t>(mIdleCallbackIndex) < mIdleObservers.Length(), |
michael@0 | 10671 | "Idle callback index exceeds array bounds!"); |
michael@0 | 10672 | IdleObserverHolder& idleObserver = mIdleObservers.ElementAt(mIdleCallbackIndex); |
michael@0 | 10673 | NotifyIdleObserver(&idleObserver, true); |
michael@0 | 10674 | mIdleCallbackIndex++; |
michael@0 | 10675 | if (NS_FAILED(ScheduleNextIdleObserverCallback())) { |
michael@0 | 10676 | NS_WARNING("Failed to set next idle observer callback."); |
michael@0 | 10677 | } |
michael@0 | 10678 | } |
michael@0 | 10679 | |
michael@0 | 10680 | nsresult |
michael@0 | 10681 | nsGlobalWindow::ScheduleNextIdleObserverCallback() |
michael@0 | 10682 | { |
michael@0 | 10683 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 10684 | MOZ_ASSERT(mIdleService, "No idle service!"); |
michael@0 | 10685 | |
michael@0 | 10686 | if (mIdleCallbackIndex < 0 || |
michael@0 | 10687 | static_cast<uint32_t>(mIdleCallbackIndex) >= mIdleObservers.Length()) { |
michael@0 | 10688 | return NS_OK; |
michael@0 | 10689 | } |
michael@0 | 10690 | |
michael@0 | 10691 | IdleObserverHolder& idleObserver = |
michael@0 | 10692 | mIdleObservers.ElementAt(mIdleCallbackIndex); |
michael@0 | 10693 | |
michael@0 | 10694 | uint32_t userIdleTimeMS = 0; |
michael@0 | 10695 | nsresult rv = mIdleService->GetIdleTime(&userIdleTimeMS); |
michael@0 | 10696 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10697 | |
michael@0 | 10698 | uint32_t callbackTimeMS = 0; |
michael@0 | 10699 | if (idleObserver.mTimeInS * 1000 + mIdleFuzzFactor > userIdleTimeMS) { |
michael@0 | 10700 | callbackTimeMS = idleObserver.mTimeInS * 1000 - userIdleTimeMS + mIdleFuzzFactor; |
michael@0 | 10701 | } |
michael@0 | 10702 | |
michael@0 | 10703 | mIdleTimer->Cancel(); |
michael@0 | 10704 | rv = mIdleTimer->InitWithFuncCallback(IdleObserverTimerCallback, |
michael@0 | 10705 | this, |
michael@0 | 10706 | callbackTimeMS, |
michael@0 | 10707 | nsITimer::TYPE_ONE_SHOT); |
michael@0 | 10708 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10709 | |
michael@0 | 10710 | return NS_OK; |
michael@0 | 10711 | } |
michael@0 | 10712 | |
michael@0 | 10713 | uint32_t |
michael@0 | 10714 | nsGlobalWindow::GetFuzzTimeMS() |
michael@0 | 10715 | { |
michael@0 | 10716 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 10717 | |
michael@0 | 10718 | if (sIdleObserversAPIFuzzTimeDisabled) { |
michael@0 | 10719 | return 0; |
michael@0 | 10720 | } |
michael@0 | 10721 | |
michael@0 | 10722 | uint32_t randNum = MAX_IDLE_FUZZ_TIME_MS; |
michael@0 | 10723 | size_t nbytes = PR_GetRandomNoise(&randNum, sizeof(randNum)); |
michael@0 | 10724 | if (nbytes != sizeof(randNum)) { |
michael@0 | 10725 | NS_WARNING("PR_GetRandomNoise(...) Not implemented or no available noise!"); |
michael@0 | 10726 | return MAX_IDLE_FUZZ_TIME_MS; |
michael@0 | 10727 | } |
michael@0 | 10728 | |
michael@0 | 10729 | if (randNum > MAX_IDLE_FUZZ_TIME_MS) { |
michael@0 | 10730 | randNum %= MAX_IDLE_FUZZ_TIME_MS; |
michael@0 | 10731 | } |
michael@0 | 10732 | |
michael@0 | 10733 | return randNum; |
michael@0 | 10734 | } |
michael@0 | 10735 | |
michael@0 | 10736 | nsresult |
michael@0 | 10737 | nsGlobalWindow::ScheduleActiveTimerCallback() |
michael@0 | 10738 | { |
michael@0 | 10739 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 10740 | |
michael@0 | 10741 | if (!mAddActiveEventFuzzTime) { |
michael@0 | 10742 | return HandleIdleActiveEvent(); |
michael@0 | 10743 | } |
michael@0 | 10744 | |
michael@0 | 10745 | MOZ_ASSERT(mIdleTimer); |
michael@0 | 10746 | mIdleTimer->Cancel(); |
michael@0 | 10747 | |
michael@0 | 10748 | uint32_t fuzzFactorInMS = GetFuzzTimeMS(); |
michael@0 | 10749 | nsresult rv = mIdleTimer->InitWithFuncCallback(IdleActiveTimerCallback, |
michael@0 | 10750 | this, |
michael@0 | 10751 | fuzzFactorInMS, |
michael@0 | 10752 | nsITimer::TYPE_ONE_SHOT); |
michael@0 | 10753 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10754 | return NS_OK; |
michael@0 | 10755 | } |
michael@0 | 10756 | |
michael@0 | 10757 | nsresult |
michael@0 | 10758 | nsGlobalWindow::HandleIdleActiveEvent() |
michael@0 | 10759 | { |
michael@0 | 10760 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 10761 | |
michael@0 | 10762 | if (mCurrentlyIdle) { |
michael@0 | 10763 | mIdleCallbackIndex = 0; |
michael@0 | 10764 | mIdleFuzzFactor = GetFuzzTimeMS(); |
michael@0 | 10765 | nsresult rv = ScheduleNextIdleObserverCallback(); |
michael@0 | 10766 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10767 | return NS_OK; |
michael@0 | 10768 | } |
michael@0 | 10769 | |
michael@0 | 10770 | mIdleCallbackIndex = -1; |
michael@0 | 10771 | MOZ_ASSERT(mIdleTimer); |
michael@0 | 10772 | mIdleTimer->Cancel(); |
michael@0 | 10773 | nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers); |
michael@0 | 10774 | while (iter.HasMore()) { |
michael@0 | 10775 | IdleObserverHolder& idleObserver = iter.GetNext(); |
michael@0 | 10776 | if (idleObserver.mPrevNotificationIdle) { |
michael@0 | 10777 | NotifyIdleObserver(&idleObserver, false); |
michael@0 | 10778 | } |
michael@0 | 10779 | } |
michael@0 | 10780 | |
michael@0 | 10781 | return NS_OK; |
michael@0 | 10782 | } |
michael@0 | 10783 | |
michael@0 | 10784 | nsGlobalWindow::SlowScriptResponse |
michael@0 | 10785 | nsGlobalWindow::ShowSlowScriptDialog() |
michael@0 | 10786 | { |
michael@0 | 10787 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 10788 | |
michael@0 | 10789 | nsresult rv; |
michael@0 | 10790 | AutoJSContext cx; |
michael@0 | 10791 | |
michael@0 | 10792 | // If it isn't safe to run script, then it isn't safe to bring up the prompt |
michael@0 | 10793 | // (since that spins the event loop). In that (rare) case, we just kill the |
michael@0 | 10794 | // script and report a warning. |
michael@0 | 10795 | if (!nsContentUtils::IsSafeToRunScript()) { |
michael@0 | 10796 | JS_ReportWarning(cx, "A long running script was terminated"); |
michael@0 | 10797 | return KillSlowScript; |
michael@0 | 10798 | } |
michael@0 | 10799 | |
michael@0 | 10800 | // If our document is not active, just kill the script: we've been unloaded |
michael@0 | 10801 | if (!HasActiveDocument()) { |
michael@0 | 10802 | return KillSlowScript; |
michael@0 | 10803 | } |
michael@0 | 10804 | |
michael@0 | 10805 | // Get the nsIPrompt interface from the docshell |
michael@0 | 10806 | nsCOMPtr<nsIDocShell> ds = GetDocShell(); |
michael@0 | 10807 | NS_ENSURE_TRUE(ds, KillSlowScript); |
michael@0 | 10808 | nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds); |
michael@0 | 10809 | NS_ENSURE_TRUE(prompt, KillSlowScript); |
michael@0 | 10810 | |
michael@0 | 10811 | // Check if we should offer the option to debug |
michael@0 | 10812 | JS::AutoFilename filename; |
michael@0 | 10813 | unsigned lineno; |
michael@0 | 10814 | bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno); |
michael@0 | 10815 | |
michael@0 | 10816 | bool debugPossible = hasFrame && js::CanCallContextDebugHandler(cx); |
michael@0 | 10817 | #ifdef MOZ_JSDEBUGGER |
michael@0 | 10818 | // Get the debugger service if necessary. |
michael@0 | 10819 | if (debugPossible) { |
michael@0 | 10820 | bool jsds_IsOn = false; |
michael@0 | 10821 | const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1"; |
michael@0 | 10822 | nsCOMPtr<jsdIExecutionHook> jsdHook; |
michael@0 | 10823 | nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv); |
michael@0 | 10824 | |
michael@0 | 10825 | // Check if there's a user for the debugger service that's 'on' for us |
michael@0 | 10826 | if (NS_SUCCEEDED(rv)) { |
michael@0 | 10827 | jsds->GetDebuggerHook(getter_AddRefs(jsdHook)); |
michael@0 | 10828 | jsds->GetIsOn(&jsds_IsOn); |
michael@0 | 10829 | } |
michael@0 | 10830 | |
michael@0 | 10831 | // If there is a debug handler registered for this runtime AND |
michael@0 | 10832 | // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs))) |
michael@0 | 10833 | // then something useful will be done with our request to debug. |
michael@0 | 10834 | debugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn); |
michael@0 | 10835 | } |
michael@0 | 10836 | #endif |
michael@0 | 10837 | |
michael@0 | 10838 | // Get localizable strings |
michael@0 | 10839 | nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg; |
michael@0 | 10840 | |
michael@0 | 10841 | rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10842 | "KillScriptTitle", |
michael@0 | 10843 | title); |
michael@0 | 10844 | |
michael@0 | 10845 | nsresult tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10846 | "StopScriptButton", |
michael@0 | 10847 | stopButton); |
michael@0 | 10848 | if (NS_FAILED(tmp)) { |
michael@0 | 10849 | rv = tmp; |
michael@0 | 10850 | } |
michael@0 | 10851 | |
michael@0 | 10852 | tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10853 | "WaitForScriptButton", |
michael@0 | 10854 | waitButton); |
michael@0 | 10855 | if (NS_FAILED(tmp)) { |
michael@0 | 10856 | rv = tmp; |
michael@0 | 10857 | } |
michael@0 | 10858 | |
michael@0 | 10859 | tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10860 | "DontAskAgain", |
michael@0 | 10861 | neverShowDlg); |
michael@0 | 10862 | if (NS_FAILED(tmp)) { |
michael@0 | 10863 | rv = tmp; |
michael@0 | 10864 | } |
michael@0 | 10865 | |
michael@0 | 10866 | |
michael@0 | 10867 | if (debugPossible) { |
michael@0 | 10868 | tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10869 | "DebugScriptButton", |
michael@0 | 10870 | debugButton); |
michael@0 | 10871 | if (NS_FAILED(tmp)) { |
michael@0 | 10872 | rv = tmp; |
michael@0 | 10873 | } |
michael@0 | 10874 | |
michael@0 | 10875 | tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10876 | "KillScriptWithDebugMessage", |
michael@0 | 10877 | msg); |
michael@0 | 10878 | if (NS_FAILED(tmp)) { |
michael@0 | 10879 | rv = tmp; |
michael@0 | 10880 | } |
michael@0 | 10881 | } |
michael@0 | 10882 | else { |
michael@0 | 10883 | tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10884 | "KillScriptMessage", |
michael@0 | 10885 | msg); |
michael@0 | 10886 | if (NS_FAILED(tmp)) { |
michael@0 | 10887 | rv = tmp; |
michael@0 | 10888 | } |
michael@0 | 10889 | } |
michael@0 | 10890 | |
michael@0 | 10891 | // GetStringFromName can return NS_OK and still give nullptr string |
michael@0 | 10892 | if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton || |
michael@0 | 10893 | (!debugButton && debugPossible) || !neverShowDlg) { |
michael@0 | 10894 | NS_ERROR("Failed to get localized strings."); |
michael@0 | 10895 | return ContinueSlowScript; |
michael@0 | 10896 | } |
michael@0 | 10897 | |
michael@0 | 10898 | // Append file and line number information, if available |
michael@0 | 10899 | if (filename.get()) { |
michael@0 | 10900 | nsXPIDLString scriptLocation; |
michael@0 | 10901 | NS_ConvertUTF8toUTF16 filenameUTF16(filename.get()); |
michael@0 | 10902 | const char16_t *formatParams[] = { filenameUTF16.get() }; |
michael@0 | 10903 | rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
michael@0 | 10904 | "KillScriptLocation", |
michael@0 | 10905 | formatParams, |
michael@0 | 10906 | scriptLocation); |
michael@0 | 10907 | |
michael@0 | 10908 | if (NS_SUCCEEDED(rv) && scriptLocation) { |
michael@0 | 10909 | msg.AppendLiteral("\n\n"); |
michael@0 | 10910 | msg.Append(scriptLocation); |
michael@0 | 10911 | msg.Append(':'); |
michael@0 | 10912 | msg.AppendInt(lineno); |
michael@0 | 10913 | } |
michael@0 | 10914 | } |
michael@0 | 10915 | |
michael@0 | 10916 | int32_t buttonPressed = 0; // In case the user exits dialog by clicking X. |
michael@0 | 10917 | bool neverShowDlgChk = false; |
michael@0 | 10918 | uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT + |
michael@0 | 10919 | (nsIPrompt::BUTTON_TITLE_IS_STRING * |
michael@0 | 10920 | (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1)); |
michael@0 | 10921 | |
michael@0 | 10922 | // Add a third button if necessary. |
michael@0 | 10923 | if (debugPossible) |
michael@0 | 10924 | buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2; |
michael@0 | 10925 | |
michael@0 | 10926 | // Null out the operation callback while we're re-entering JS here. |
michael@0 | 10927 | JSRuntime* rt = JS_GetRuntime(cx); |
michael@0 | 10928 | JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr); |
michael@0 | 10929 | |
michael@0 | 10930 | // Open the dialog. |
michael@0 | 10931 | rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton, |
michael@0 | 10932 | debugButton, neverShowDlg, &neverShowDlgChk, |
michael@0 | 10933 | &buttonPressed); |
michael@0 | 10934 | |
michael@0 | 10935 | JS_SetInterruptCallback(rt, old); |
michael@0 | 10936 | |
michael@0 | 10937 | if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) { |
michael@0 | 10938 | return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript; |
michael@0 | 10939 | } |
michael@0 | 10940 | if ((buttonPressed == 2) && debugPossible) { |
michael@0 | 10941 | return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript; |
michael@0 | 10942 | } |
michael@0 | 10943 | JS_ClearPendingException(cx); |
michael@0 | 10944 | return KillSlowScript; |
michael@0 | 10945 | } |
michael@0 | 10946 | |
michael@0 | 10947 | uint32_t |
michael@0 | 10948 | nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver) |
michael@0 | 10949 | { |
michael@0 | 10950 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 10951 | MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated."); |
michael@0 | 10952 | |
michael@0 | 10953 | uint32_t i = 0; |
michael@0 | 10954 | nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers); |
michael@0 | 10955 | while (iter.HasMore()) { |
michael@0 | 10956 | IdleObserverHolder& idleObserver = iter.GetNext(); |
michael@0 | 10957 | if (idleObserver.mTimeInS > aIdleObserver->mTimeInS) { |
michael@0 | 10958 | break; |
michael@0 | 10959 | } |
michael@0 | 10960 | i++; |
michael@0 | 10961 | MOZ_ASSERT(i <= mIdleObservers.Length(), "Array index out of bounds error."); |
michael@0 | 10962 | } |
michael@0 | 10963 | |
michael@0 | 10964 | return i; |
michael@0 | 10965 | } |
michael@0 | 10966 | |
michael@0 | 10967 | nsresult |
michael@0 | 10968 | nsGlobalWindow::RegisterIdleObserver(nsIIdleObserver* aIdleObserver) |
michael@0 | 10969 | { |
michael@0 | 10970 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 10971 | |
michael@0 | 10972 | nsresult rv; |
michael@0 | 10973 | if (mIdleObservers.IsEmpty()) { |
michael@0 | 10974 | mIdleService = do_GetService("@mozilla.org/widget/idleservice;1", &rv); |
michael@0 | 10975 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10976 | |
michael@0 | 10977 | rv = mIdleService->AddIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); |
michael@0 | 10978 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10979 | |
michael@0 | 10980 | if (!mIdleTimer) { |
michael@0 | 10981 | mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); |
michael@0 | 10982 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10983 | } else { |
michael@0 | 10984 | mIdleTimer->Cancel(); |
michael@0 | 10985 | } |
michael@0 | 10986 | } |
michael@0 | 10987 | |
michael@0 | 10988 | MOZ_ASSERT(mIdleService); |
michael@0 | 10989 | MOZ_ASSERT(mIdleTimer); |
michael@0 | 10990 | |
michael@0 | 10991 | IdleObserverHolder tmpIdleObserver; |
michael@0 | 10992 | tmpIdleObserver.mIdleObserver = aIdleObserver; |
michael@0 | 10993 | rv = aIdleObserver->GetTime(&tmpIdleObserver.mTimeInS); |
michael@0 | 10994 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 10995 | NS_ENSURE_ARG_MAX(tmpIdleObserver.mTimeInS, UINT32_MAX / 1000); |
michael@0 | 10996 | NS_ENSURE_ARG_MIN(tmpIdleObserver.mTimeInS, MIN_IDLE_NOTIFICATION_TIME_S); |
michael@0 | 10997 | |
michael@0 | 10998 | uint32_t insertAtIndex = FindInsertionIndex(&tmpIdleObserver); |
michael@0 | 10999 | if (insertAtIndex == mIdleObservers.Length()) { |
michael@0 | 11000 | mIdleObservers.AppendElement(tmpIdleObserver); |
michael@0 | 11001 | } |
michael@0 | 11002 | else { |
michael@0 | 11003 | mIdleObservers.InsertElementAt(insertAtIndex, tmpIdleObserver); |
michael@0 | 11004 | } |
michael@0 | 11005 | |
michael@0 | 11006 | bool userIsIdle = false; |
michael@0 | 11007 | rv = nsContentUtils::IsUserIdle(MIN_IDLE_NOTIFICATION_TIME_S, &userIsIdle); |
michael@0 | 11008 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11009 | |
michael@0 | 11010 | // Special case. First idle observer added to empty list while the user is idle. |
michael@0 | 11011 | // Haven't received 'idle' topic notification from slow idle service yet. |
michael@0 | 11012 | // Need to wait for the idle notification and then notify idle observers in the list. |
michael@0 | 11013 | if (userIsIdle && mIdleCallbackIndex == -1) { |
michael@0 | 11014 | return NS_OK; |
michael@0 | 11015 | } |
michael@0 | 11016 | |
michael@0 | 11017 | if (!mCurrentlyIdle) { |
michael@0 | 11018 | return NS_OK; |
michael@0 | 11019 | } |
michael@0 | 11020 | |
michael@0 | 11021 | MOZ_ASSERT(mIdleCallbackIndex >= 0); |
michael@0 | 11022 | |
michael@0 | 11023 | if (static_cast<int32_t>(insertAtIndex) < mIdleCallbackIndex) { |
michael@0 | 11024 | IdleObserverHolder& idleObserver = mIdleObservers.ElementAt(insertAtIndex); |
michael@0 | 11025 | NotifyIdleObserver(&idleObserver, true); |
michael@0 | 11026 | mIdleCallbackIndex++; |
michael@0 | 11027 | return NS_OK; |
michael@0 | 11028 | } |
michael@0 | 11029 | |
michael@0 | 11030 | if (static_cast<int32_t>(insertAtIndex) == mIdleCallbackIndex) { |
michael@0 | 11031 | mIdleTimer->Cancel(); |
michael@0 | 11032 | rv = ScheduleNextIdleObserverCallback(); |
michael@0 | 11033 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11034 | } |
michael@0 | 11035 | return NS_OK; |
michael@0 | 11036 | } |
michael@0 | 11037 | |
michael@0 | 11038 | nsresult |
michael@0 | 11039 | nsGlobalWindow::FindIndexOfElementToRemove(nsIIdleObserver* aIdleObserver, |
michael@0 | 11040 | int32_t* aRemoveElementIndex) |
michael@0 | 11041 | { |
michael@0 | 11042 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 11043 | MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated."); |
michael@0 | 11044 | |
michael@0 | 11045 | *aRemoveElementIndex = 0; |
michael@0 | 11046 | if (mIdleObservers.IsEmpty()) { |
michael@0 | 11047 | return NS_ERROR_FAILURE; |
michael@0 | 11048 | } |
michael@0 | 11049 | |
michael@0 | 11050 | uint32_t aIdleObserverTimeInS; |
michael@0 | 11051 | nsresult rv = aIdleObserver->GetTime(&aIdleObserverTimeInS); |
michael@0 | 11052 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11053 | NS_ENSURE_ARG_MIN(aIdleObserverTimeInS, MIN_IDLE_NOTIFICATION_TIME_S); |
michael@0 | 11054 | |
michael@0 | 11055 | nsTObserverArray<IdleObserverHolder>::ForwardIterator iter(mIdleObservers); |
michael@0 | 11056 | while (iter.HasMore()) { |
michael@0 | 11057 | IdleObserverHolder& idleObserver = iter.GetNext(); |
michael@0 | 11058 | if (idleObserver.mTimeInS == aIdleObserverTimeInS && |
michael@0 | 11059 | idleObserver.mIdleObserver == aIdleObserver ) { |
michael@0 | 11060 | break; |
michael@0 | 11061 | } |
michael@0 | 11062 | (*aRemoveElementIndex)++; |
michael@0 | 11063 | } |
michael@0 | 11064 | return static_cast<uint32_t>(*aRemoveElementIndex) >= mIdleObservers.Length() ? |
michael@0 | 11065 | NS_ERROR_FAILURE : NS_OK; |
michael@0 | 11066 | } |
michael@0 | 11067 | |
michael@0 | 11068 | nsresult |
michael@0 | 11069 | nsGlobalWindow::UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) |
michael@0 | 11070 | { |
michael@0 | 11071 | MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); |
michael@0 | 11072 | |
michael@0 | 11073 | int32_t removeElementIndex; |
michael@0 | 11074 | nsresult rv = FindIndexOfElementToRemove(aIdleObserver, &removeElementIndex); |
michael@0 | 11075 | if (NS_FAILED(rv)) { |
michael@0 | 11076 | NS_WARNING("Idle observer not found in list of idle observers. No idle observer removed."); |
michael@0 | 11077 | return NS_OK; |
michael@0 | 11078 | } |
michael@0 | 11079 | mIdleObservers.RemoveElementAt(removeElementIndex); |
michael@0 | 11080 | |
michael@0 | 11081 | MOZ_ASSERT(mIdleTimer); |
michael@0 | 11082 | if (mIdleObservers.IsEmpty() && mIdleService) { |
michael@0 | 11083 | rv = mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); |
michael@0 | 11084 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11085 | mIdleService = nullptr; |
michael@0 | 11086 | |
michael@0 | 11087 | mIdleTimer->Cancel(); |
michael@0 | 11088 | mIdleCallbackIndex = -1; |
michael@0 | 11089 | return NS_OK; |
michael@0 | 11090 | } |
michael@0 | 11091 | |
michael@0 | 11092 | if (!mCurrentlyIdle) { |
michael@0 | 11093 | return NS_OK; |
michael@0 | 11094 | } |
michael@0 | 11095 | |
michael@0 | 11096 | if (removeElementIndex < mIdleCallbackIndex) { |
michael@0 | 11097 | mIdleCallbackIndex--; |
michael@0 | 11098 | return NS_OK; |
michael@0 | 11099 | } |
michael@0 | 11100 | |
michael@0 | 11101 | if (removeElementIndex != mIdleCallbackIndex) { |
michael@0 | 11102 | return NS_OK; |
michael@0 | 11103 | } |
michael@0 | 11104 | |
michael@0 | 11105 | mIdleTimer->Cancel(); |
michael@0 | 11106 | |
michael@0 | 11107 | // If the last element in the array had been notified then decrement |
michael@0 | 11108 | // mIdleCallbackIndex because an idle was removed from the list of |
michael@0 | 11109 | // idle observers. |
michael@0 | 11110 | // Example: add idle observer with time 1, 2, 3, |
michael@0 | 11111 | // Idle notifications for idle observers with time 1, 2, 3 are complete |
michael@0 | 11112 | // Remove idle observer with time 3 while the user is still idle. |
michael@0 | 11113 | // The user never transitioned to active state. |
michael@0 | 11114 | // Add an idle observer with idle time 4 |
michael@0 | 11115 | if (static_cast<uint32_t>(mIdleCallbackIndex) == mIdleObservers.Length()) { |
michael@0 | 11116 | mIdleCallbackIndex--; |
michael@0 | 11117 | } |
michael@0 | 11118 | rv = ScheduleNextIdleObserverCallback(); |
michael@0 | 11119 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11120 | |
michael@0 | 11121 | return NS_OK; |
michael@0 | 11122 | } |
michael@0 | 11123 | |
michael@0 | 11124 | nsresult |
michael@0 | 11125 | nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, |
michael@0 | 11126 | const char16_t* aData) |
michael@0 | 11127 | { |
michael@0 | 11128 | if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) { |
michael@0 | 11129 | if (IsFrozen()) { |
michael@0 | 11130 | // if an even number of notifications arrive while we're frozen, |
michael@0 | 11131 | // we don't need to fire. |
michael@0 | 11132 | mFireOfflineStatusChangeEventOnThaw = !mFireOfflineStatusChangeEventOnThaw; |
michael@0 | 11133 | } else { |
michael@0 | 11134 | FireOfflineStatusEvent(); |
michael@0 | 11135 | } |
michael@0 | 11136 | return NS_OK; |
michael@0 | 11137 | } |
michael@0 | 11138 | |
michael@0 | 11139 | if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { |
michael@0 | 11140 | mCurrentlyIdle = true; |
michael@0 | 11141 | if (IsFrozen()) { |
michael@0 | 11142 | // need to fire only one idle event while the window is frozen. |
michael@0 | 11143 | mNotifyIdleObserversIdleOnThaw = true; |
michael@0 | 11144 | mNotifyIdleObserversActiveOnThaw = false; |
michael@0 | 11145 | } else if (IsCurrentInnerWindow()) { |
michael@0 | 11146 | HandleIdleActiveEvent(); |
michael@0 | 11147 | } |
michael@0 | 11148 | return NS_OK; |
michael@0 | 11149 | } |
michael@0 | 11150 | |
michael@0 | 11151 | if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) { |
michael@0 | 11152 | mCurrentlyIdle = false; |
michael@0 | 11153 | if (IsFrozen()) { |
michael@0 | 11154 | mNotifyIdleObserversActiveOnThaw = true; |
michael@0 | 11155 | mNotifyIdleObserversIdleOnThaw = false; |
michael@0 | 11156 | } else if (IsCurrentInnerWindow()) { |
michael@0 | 11157 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 11158 | ScheduleActiveTimerCallback(); |
michael@0 | 11159 | } |
michael@0 | 11160 | return NS_OK; |
michael@0 | 11161 | } |
michael@0 | 11162 | |
michael@0 | 11163 | if (!nsCRT::strcmp(aTopic, "dom-storage2-changed")) { |
michael@0 | 11164 | if (!IsInnerWindow() || !IsCurrentInnerWindow()) { |
michael@0 | 11165 | return NS_OK; |
michael@0 | 11166 | } |
michael@0 | 11167 | |
michael@0 | 11168 | nsIPrincipal *principal; |
michael@0 | 11169 | nsresult rv; |
michael@0 | 11170 | |
michael@0 | 11171 | nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv); |
michael@0 | 11172 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11173 | |
michael@0 | 11174 | nsCOMPtr<nsIDOMStorage> changingStorage; |
michael@0 | 11175 | rv = event->GetStorageArea(getter_AddRefs(changingStorage)); |
michael@0 | 11176 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11177 | |
michael@0 | 11178 | bool fireMozStorageChanged = false; |
michael@0 | 11179 | principal = GetPrincipal(); |
michael@0 | 11180 | if (!principal) { |
michael@0 | 11181 | return NS_OK; |
michael@0 | 11182 | } |
michael@0 | 11183 | |
michael@0 | 11184 | nsCOMPtr<nsPIDOMStorage> pistorage = do_QueryInterface(changingStorage); |
michael@0 | 11185 | |
michael@0 | 11186 | nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(GetDocShell()); |
michael@0 | 11187 | bool isPrivate = loadContext && loadContext->UsePrivateBrowsing(); |
michael@0 | 11188 | if (pistorage->IsPrivate() != isPrivate) { |
michael@0 | 11189 | return NS_OK; |
michael@0 | 11190 | } |
michael@0 | 11191 | |
michael@0 | 11192 | switch (pistorage->GetType()) |
michael@0 | 11193 | { |
michael@0 | 11194 | case nsPIDOMStorage::SessionStorage: |
michael@0 | 11195 | { |
michael@0 | 11196 | bool check = false; |
michael@0 | 11197 | |
michael@0 | 11198 | nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell()); |
michael@0 | 11199 | if (storageManager) { |
michael@0 | 11200 | nsresult rv; |
michael@0 | 11201 | nsCOMPtr<nsIURI> firstPartyIsolationURI; |
michael@0 | 11202 | rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI)); |
michael@0 | 11203 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11204 | |
michael@0 | 11205 | rv = storageManager->CheckStorageForFirstParty(firstPartyIsolationURI, |
michael@0 | 11206 | principal, changingStorage, &check); |
michael@0 | 11207 | if (NS_FAILED(rv)) { |
michael@0 | 11208 | return rv; |
michael@0 | 11209 | } |
michael@0 | 11210 | } |
michael@0 | 11211 | |
michael@0 | 11212 | if (!check) { |
michael@0 | 11213 | // This storage event is not coming from our storage or is coming |
michael@0 | 11214 | // from a different docshell, i.e. it is a clone, ignore this event. |
michael@0 | 11215 | return NS_OK; |
michael@0 | 11216 | } |
michael@0 | 11217 | |
michael@0 | 11218 | #ifdef PR_LOGGING |
michael@0 | 11219 | if (PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { |
michael@0 | 11220 | PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p", this, mSessionStorage.get(), pistorage.get()); |
michael@0 | 11221 | } |
michael@0 | 11222 | #endif |
michael@0 | 11223 | |
michael@0 | 11224 | fireMozStorageChanged = SameCOMIdentity(mSessionStorage, changingStorage); |
michael@0 | 11225 | break; |
michael@0 | 11226 | } |
michael@0 | 11227 | |
michael@0 | 11228 | case nsPIDOMStorage::LocalStorage: |
michael@0 | 11229 | { |
michael@0 | 11230 | // Allow event fire only for the same principal storages |
michael@0 | 11231 | // XXX We have to use EqualsIgnoreDomain after bug 495337 lands |
michael@0 | 11232 | nsIPrincipal* storagePrincipal = pistorage->GetPrincipal(); |
michael@0 | 11233 | |
michael@0 | 11234 | bool equals = false; |
michael@0 | 11235 | rv = storagePrincipal->Equals(principal, &equals); |
michael@0 | 11236 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11237 | |
michael@0 | 11238 | if (!equals) |
michael@0 | 11239 | return NS_OK; |
michael@0 | 11240 | |
michael@0 | 11241 | fireMozStorageChanged = SameCOMIdentity(mLocalStorage, changingStorage); |
michael@0 | 11242 | break; |
michael@0 | 11243 | } |
michael@0 | 11244 | default: |
michael@0 | 11245 | return NS_OK; |
michael@0 | 11246 | } |
michael@0 | 11247 | |
michael@0 | 11248 | // Clone the storage event included in the observer notification. We want |
michael@0 | 11249 | // to dispatch clones rather than the original event. |
michael@0 | 11250 | rv = CloneStorageEvent(fireMozStorageChanged ? |
michael@0 | 11251 | NS_LITERAL_STRING("MozStorageChanged") : |
michael@0 | 11252 | NS_LITERAL_STRING("storage"), |
michael@0 | 11253 | event); |
michael@0 | 11254 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11255 | |
michael@0 | 11256 | event->SetTrusted(true); |
michael@0 | 11257 | |
michael@0 | 11258 | if (fireMozStorageChanged) { |
michael@0 | 11259 | WidgetEvent* internalEvent = event->GetInternalNSEvent(); |
michael@0 | 11260 | internalEvent->mFlags.mOnlyChromeDispatch = true; |
michael@0 | 11261 | } |
michael@0 | 11262 | |
michael@0 | 11263 | if (IsFrozen()) { |
michael@0 | 11264 | // This window is frozen, rather than firing the events here, |
michael@0 | 11265 | // store the domain in which the change happened and fire the |
michael@0 | 11266 | // events if we're ever thawed. |
michael@0 | 11267 | |
michael@0 | 11268 | mPendingStorageEvents.AppendObject(event); |
michael@0 | 11269 | return NS_OK; |
michael@0 | 11270 | } |
michael@0 | 11271 | |
michael@0 | 11272 | bool defaultActionEnabled; |
michael@0 | 11273 | DispatchEvent((nsIDOMStorageEvent *)event, &defaultActionEnabled); |
michael@0 | 11274 | |
michael@0 | 11275 | return NS_OK; |
michael@0 | 11276 | } |
michael@0 | 11277 | |
michael@0 | 11278 | if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) { |
michael@0 | 11279 | if (mApplicationCache) |
michael@0 | 11280 | return NS_OK; |
michael@0 | 11281 | |
michael@0 | 11282 | // Instantiate the application object now. It observes update belonging to |
michael@0 | 11283 | // this window's document and correctly updates the applicationCache object |
michael@0 | 11284 | // state. |
michael@0 | 11285 | nsCOMPtr<nsIDOMOfflineResourceList> applicationCache; |
michael@0 | 11286 | GetApplicationCache(getter_AddRefs(applicationCache)); |
michael@0 | 11287 | nsCOMPtr<nsIObserver> observer = do_QueryInterface(applicationCache); |
michael@0 | 11288 | if (observer) |
michael@0 | 11289 | observer->Observe(aSubject, aTopic, aData); |
michael@0 | 11290 | |
michael@0 | 11291 | return NS_OK; |
michael@0 | 11292 | } |
michael@0 | 11293 | |
michael@0 | 11294 | #ifdef MOZ_B2G |
michael@0 | 11295 | if (!nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC) || |
michael@0 | 11296 | !nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC)) { |
michael@0 | 11297 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 11298 | if (!IsCurrentInnerWindow()) { |
michael@0 | 11299 | return NS_OK; |
michael@0 | 11300 | } |
michael@0 | 11301 | |
michael@0 | 11302 | nsCOMPtr<nsIDOMEvent> event; |
michael@0 | 11303 | NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); |
michael@0 | 11304 | nsresult rv = event->InitEvent( |
michael@0 | 11305 | !nsCRT::strcmp(aTopic, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC) |
michael@0 | 11306 | ? NETWORK_UPLOAD_EVENT_NAME |
michael@0 | 11307 | : NETWORK_DOWNLOAD_EVENT_NAME, |
michael@0 | 11308 | false, false); |
michael@0 | 11309 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11310 | |
michael@0 | 11311 | event->SetTrusted(true); |
michael@0 | 11312 | |
michael@0 | 11313 | bool dummy; |
michael@0 | 11314 | return DispatchEvent(event, &dummy); |
michael@0 | 11315 | } |
michael@0 | 11316 | #endif // MOZ_B2G |
michael@0 | 11317 | |
michael@0 | 11318 | NS_WARNING("unrecognized topic in nsGlobalWindow::Observe"); |
michael@0 | 11319 | return NS_ERROR_FAILURE; |
michael@0 | 11320 | } |
michael@0 | 11321 | |
michael@0 | 11322 | nsresult |
michael@0 | 11323 | nsGlobalWindow::CloneStorageEvent(const nsAString& aType, |
michael@0 | 11324 | nsCOMPtr<nsIDOMStorageEvent>& aEvent) |
michael@0 | 11325 | { |
michael@0 | 11326 | nsresult rv; |
michael@0 | 11327 | |
michael@0 | 11328 | bool canBubble; |
michael@0 | 11329 | bool cancelable; |
michael@0 | 11330 | nsAutoString key; |
michael@0 | 11331 | nsAutoString oldValue; |
michael@0 | 11332 | nsAutoString newValue; |
michael@0 | 11333 | nsAutoString url; |
michael@0 | 11334 | nsCOMPtr<nsIDOMStorage> storageArea; |
michael@0 | 11335 | |
michael@0 | 11336 | nsCOMPtr<nsIDOMEvent> domEvent = do_QueryInterface(aEvent, &rv); |
michael@0 | 11337 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11338 | |
michael@0 | 11339 | domEvent->GetBubbles(&canBubble); |
michael@0 | 11340 | domEvent->GetCancelable(&cancelable); |
michael@0 | 11341 | |
michael@0 | 11342 | aEvent->GetKey(key); |
michael@0 | 11343 | aEvent->GetOldValue(oldValue); |
michael@0 | 11344 | aEvent->GetNewValue(newValue); |
michael@0 | 11345 | aEvent->GetUrl(url); |
michael@0 | 11346 | aEvent->GetStorageArea(getter_AddRefs(storageArea)); |
michael@0 | 11347 | |
michael@0 | 11348 | NS_NewDOMStorageEvent(getter_AddRefs(domEvent), this, nullptr, nullptr); |
michael@0 | 11349 | aEvent = do_QueryInterface(domEvent); |
michael@0 | 11350 | return aEvent->InitStorageEvent(aType, canBubble, cancelable, |
michael@0 | 11351 | key, oldValue, newValue, |
michael@0 | 11352 | url, storageArea); |
michael@0 | 11353 | } |
michael@0 | 11354 | |
michael@0 | 11355 | nsresult |
michael@0 | 11356 | nsGlobalWindow::FireDelayedDOMEvents() |
michael@0 | 11357 | { |
michael@0 | 11358 | FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED); |
michael@0 | 11359 | |
michael@0 | 11360 | for (int32_t i = 0; i < mPendingStorageEvents.Count(); ++i) { |
michael@0 | 11361 | Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr); |
michael@0 | 11362 | } |
michael@0 | 11363 | |
michael@0 | 11364 | if (mApplicationCache) { |
michael@0 | 11365 | static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents(); |
michael@0 | 11366 | } |
michael@0 | 11367 | |
michael@0 | 11368 | if (mFireOfflineStatusChangeEventOnThaw) { |
michael@0 | 11369 | mFireOfflineStatusChangeEventOnThaw = false; |
michael@0 | 11370 | FireOfflineStatusEvent(); |
michael@0 | 11371 | } |
michael@0 | 11372 | |
michael@0 | 11373 | if (mNotifyIdleObserversIdleOnThaw) { |
michael@0 | 11374 | mNotifyIdleObserversIdleOnThaw = false; |
michael@0 | 11375 | HandleIdleActiveEvent(); |
michael@0 | 11376 | } |
michael@0 | 11377 | |
michael@0 | 11378 | if (mNotifyIdleObserversActiveOnThaw) { |
michael@0 | 11379 | mNotifyIdleObserversActiveOnThaw = false; |
michael@0 | 11380 | ScheduleActiveTimerCallback(); |
michael@0 | 11381 | } |
michael@0 | 11382 | |
michael@0 | 11383 | nsCOMPtr<nsIDocShell> docShell = GetDocShell(); |
michael@0 | 11384 | if (docShell) { |
michael@0 | 11385 | int32_t childCount = 0; |
michael@0 | 11386 | docShell->GetChildCount(&childCount); |
michael@0 | 11387 | |
michael@0 | 11388 | for (int32_t i = 0; i < childCount; ++i) { |
michael@0 | 11389 | nsCOMPtr<nsIDocShellTreeItem> childShell; |
michael@0 | 11390 | docShell->GetChildAt(i, getter_AddRefs(childShell)); |
michael@0 | 11391 | NS_ASSERTION(childShell, "null child shell"); |
michael@0 | 11392 | |
michael@0 | 11393 | nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell); |
michael@0 | 11394 | if (pWin) { |
michael@0 | 11395 | nsGlobalWindow *win = |
michael@0 | 11396 | static_cast<nsGlobalWindow*> |
michael@0 | 11397 | (static_cast<nsPIDOMWindow*>(pWin)); |
michael@0 | 11398 | win->FireDelayedDOMEvents(); |
michael@0 | 11399 | } |
michael@0 | 11400 | } |
michael@0 | 11401 | } |
michael@0 | 11402 | |
michael@0 | 11403 | return NS_OK; |
michael@0 | 11404 | } |
michael@0 | 11405 | |
michael@0 | 11406 | //***************************************************************************** |
michael@0 | 11407 | // nsGlobalWindow: Window Control Functions |
michael@0 | 11408 | //***************************************************************************** |
michael@0 | 11409 | |
michael@0 | 11410 | nsIDOMWindow * |
michael@0 | 11411 | nsGlobalWindow::GetParentInternal() |
michael@0 | 11412 | { |
michael@0 | 11413 | if (IsInnerWindow()) { |
michael@0 | 11414 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 11415 | if (!outer) { |
michael@0 | 11416 | NS_WARNING("No outer window available!"); |
michael@0 | 11417 | return nullptr; |
michael@0 | 11418 | } |
michael@0 | 11419 | return outer->GetParentInternal(); |
michael@0 | 11420 | } |
michael@0 | 11421 | |
michael@0 | 11422 | nsCOMPtr<nsIDOMWindow> parent; |
michael@0 | 11423 | GetParent(getter_AddRefs(parent)); |
michael@0 | 11424 | |
michael@0 | 11425 | if (parent && parent != static_cast<nsIDOMWindow *>(this)) { |
michael@0 | 11426 | return parent; |
michael@0 | 11427 | } |
michael@0 | 11428 | |
michael@0 | 11429 | return nullptr; |
michael@0 | 11430 | } |
michael@0 | 11431 | |
michael@0 | 11432 | void |
michael@0 | 11433 | nsGlobalWindow::UnblockScriptedClosing() |
michael@0 | 11434 | { |
michael@0 | 11435 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 11436 | mBlockScriptedClosingFlag = false; |
michael@0 | 11437 | } |
michael@0 | 11438 | |
michael@0 | 11439 | class AutoUnblockScriptClosing |
michael@0 | 11440 | { |
michael@0 | 11441 | private: |
michael@0 | 11442 | nsRefPtr<nsGlobalWindow> mWin; |
michael@0 | 11443 | public: |
michael@0 | 11444 | AutoUnblockScriptClosing(nsGlobalWindow* aWin) |
michael@0 | 11445 | : mWin(aWin) |
michael@0 | 11446 | { |
michael@0 | 11447 | MOZ_ASSERT(mWin); |
michael@0 | 11448 | MOZ_ASSERT(mWin->IsOuterWindow()); |
michael@0 | 11449 | } |
michael@0 | 11450 | ~AutoUnblockScriptClosing() |
michael@0 | 11451 | { |
michael@0 | 11452 | void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing; |
michael@0 | 11453 | NS_DispatchToCurrentThread(NS_NewRunnableMethod(mWin, run)); |
michael@0 | 11454 | } |
michael@0 | 11455 | }; |
michael@0 | 11456 | |
michael@0 | 11457 | nsresult |
michael@0 | 11458 | nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, |
michael@0 | 11459 | const nsAString& aOptions, bool aDialog, |
michael@0 | 11460 | bool aContentModal, bool aCalledNoScript, |
michael@0 | 11461 | bool aDoJSFixups, bool aNavigate, |
michael@0 | 11462 | nsIArray *argv, |
michael@0 | 11463 | nsISupports *aExtraArgument, |
michael@0 | 11464 | nsIPrincipal *aCalleePrincipal, |
michael@0 | 11465 | JSContext *aJSCallerContext, |
michael@0 | 11466 | nsIDOMWindow **aReturn) |
michael@0 | 11467 | { |
michael@0 | 11468 | FORWARD_TO_OUTER(OpenInternal, (aUrl, aName, aOptions, aDialog, |
michael@0 | 11469 | aContentModal, aCalledNoScript, aDoJSFixups, |
michael@0 | 11470 | aNavigate, argv, aExtraArgument, |
michael@0 | 11471 | aCalleePrincipal, aJSCallerContext, aReturn), |
michael@0 | 11472 | NS_ERROR_NOT_INITIALIZED); |
michael@0 | 11473 | |
michael@0 | 11474 | #ifdef DEBUG |
michael@0 | 11475 | uint32_t argc = 0; |
michael@0 | 11476 | if (argv) |
michael@0 | 11477 | argv->GetLength(&argc); |
michael@0 | 11478 | #endif |
michael@0 | 11479 | NS_PRECONDITION(!aExtraArgument || (!argv && argc == 0), |
michael@0 | 11480 | "Can't pass in arguments both ways"); |
michael@0 | 11481 | NS_PRECONDITION(!aCalledNoScript || (!argv && argc == 0), |
michael@0 | 11482 | "Can't pass JS args when called via the noscript methods"); |
michael@0 | 11483 | NS_PRECONDITION(!aJSCallerContext || !aCalledNoScript, |
michael@0 | 11484 | "Shouldn't have caller context when called noscript"); |
michael@0 | 11485 | |
michael@0 | 11486 | mozilla::Maybe<AutoUnblockScriptClosing> closeUnblocker; |
michael@0 | 11487 | |
michael@0 | 11488 | // Calls to window.open from script should navigate. |
michael@0 | 11489 | MOZ_ASSERT(aCalledNoScript || aNavigate); |
michael@0 | 11490 | |
michael@0 | 11491 | *aReturn = nullptr; |
michael@0 | 11492 | |
michael@0 | 11493 | nsCOMPtr<nsIWebBrowserChrome> chrome = GetWebBrowserChrome(); |
michael@0 | 11494 | if (!chrome) { |
michael@0 | 11495 | // No chrome means we don't want to go through with this open call |
michael@0 | 11496 | // -- see nsIWindowWatcher.idl |
michael@0 | 11497 | return NS_ERROR_NOT_AVAILABLE; |
michael@0 | 11498 | } |
michael@0 | 11499 | |
michael@0 | 11500 | NS_ASSERTION(mDocShell, "Must have docshell here"); |
michael@0 | 11501 | |
michael@0 | 11502 | // Popups from apps are never blocked. |
michael@0 | 11503 | bool isApp = false; |
michael@0 | 11504 | if (mDoc) { |
michael@0 | 11505 | isApp = mDoc->NodePrincipal()->GetAppStatus() >= |
michael@0 | 11506 | nsIPrincipal::APP_STATUS_INSTALLED; |
michael@0 | 11507 | } |
michael@0 | 11508 | |
michael@0 | 11509 | const bool checkForPopup = !nsContentUtils::IsCallerChrome() && |
michael@0 | 11510 | !isApp && !aDialog && !WindowExists(aName, !aCalledNoScript); |
michael@0 | 11511 | |
michael@0 | 11512 | // Note: it's very important that this be an nsXPIDLCString, since we want |
michael@0 | 11513 | // .get() on it to return nullptr until we write stuff to it. The window |
michael@0 | 11514 | // watcher expects a null URL string if there is no URL to load. |
michael@0 | 11515 | nsXPIDLCString url; |
michael@0 | 11516 | nsresult rv = NS_OK; |
michael@0 | 11517 | |
michael@0 | 11518 | // It's important to do this security check before determining whether this |
michael@0 | 11519 | // window opening should be blocked, to ensure that we don't FireAbuseEvents |
michael@0 | 11520 | // for a window opening that wouldn't have succeeded in the first place. |
michael@0 | 11521 | if (!aUrl.IsEmpty()) { |
michael@0 | 11522 | AppendUTF16toUTF8(aUrl, url); |
michael@0 | 11523 | |
michael@0 | 11524 | // It's safe to skip the security check below if we're not a dialog |
michael@0 | 11525 | // because window.openDialog is not callable from content script. See bug |
michael@0 | 11526 | // 56851. |
michael@0 | 11527 | // |
michael@0 | 11528 | // If we're not navigating, we assume that whoever *does* navigate the |
michael@0 | 11529 | // window will do a security check of their own. |
michael@0 | 11530 | if (url.get() && !aDialog && aNavigate) |
michael@0 | 11531 | rv = SecurityCheckURL(url.get()); |
michael@0 | 11532 | } |
michael@0 | 11533 | |
michael@0 | 11534 | if (NS_FAILED(rv)) |
michael@0 | 11535 | return rv; |
michael@0 | 11536 | |
michael@0 | 11537 | PopupControlState abuseLevel = gPopupControlState; |
michael@0 | 11538 | if (checkForPopup) { |
michael@0 | 11539 | abuseLevel = RevisePopupAbuseLevel(abuseLevel); |
michael@0 | 11540 | if (abuseLevel >= openAbused) { |
michael@0 | 11541 | if (aJSCallerContext) { |
michael@0 | 11542 | // If script in some other window is doing a window.open on us and |
michael@0 | 11543 | // it's being blocked, then it's OK to close us afterwards, probably. |
michael@0 | 11544 | // But if we're doing a window.open on ourselves and block the popup, |
michael@0 | 11545 | // prevent this window from closing until after this script terminates |
michael@0 | 11546 | // so that whatever popup blocker UI the app has will be visible. |
michael@0 | 11547 | if (mContext == GetScriptContextFromJSContext(aJSCallerContext)) { |
michael@0 | 11548 | mBlockScriptedClosingFlag = true; |
michael@0 | 11549 | closeUnblocker.construct(this); |
michael@0 | 11550 | } |
michael@0 | 11551 | } |
michael@0 | 11552 | |
michael@0 | 11553 | FireAbuseEvents(true, false, aUrl, aName, aOptions); |
michael@0 | 11554 | return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE; |
michael@0 | 11555 | } |
michael@0 | 11556 | } |
michael@0 | 11557 | |
michael@0 | 11558 | nsCOMPtr<nsIDOMWindow> domReturn; |
michael@0 | 11559 | |
michael@0 | 11560 | nsCOMPtr<nsIWindowWatcher> wwatch = |
michael@0 | 11561 | do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); |
michael@0 | 11562 | NS_ENSURE_TRUE(wwatch, rv); |
michael@0 | 11563 | |
michael@0 | 11564 | NS_ConvertUTF16toUTF8 options(aOptions); |
michael@0 | 11565 | NS_ConvertUTF16toUTF8 name(aName); |
michael@0 | 11566 | |
michael@0 | 11567 | const char *options_ptr = aOptions.IsEmpty() ? nullptr : options.get(); |
michael@0 | 11568 | const char *name_ptr = aName.IsEmpty() ? nullptr : name.get(); |
michael@0 | 11569 | |
michael@0 | 11570 | nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch)); |
michael@0 | 11571 | NS_ENSURE_STATE(pwwatch); |
michael@0 | 11572 | |
michael@0 | 11573 | { |
michael@0 | 11574 | // Reset popup state while opening a window to prevent the |
michael@0 | 11575 | // current state from being active the whole time a modal |
michael@0 | 11576 | // dialog is open. |
michael@0 | 11577 | nsAutoPopupStatePusher popupStatePusher(openAbused, true); |
michael@0 | 11578 | |
michael@0 | 11579 | if (!aCalledNoScript) { |
michael@0 | 11580 | // We asserted at the top of this function that aNavigate is true for |
michael@0 | 11581 | // !aCalledNoScript. |
michael@0 | 11582 | rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr, |
michael@0 | 11583 | /* aCalledFromScript = */ true, |
michael@0 | 11584 | aDialog, aNavigate, argv, |
michael@0 | 11585 | getter_AddRefs(domReturn)); |
michael@0 | 11586 | } else { |
michael@0 | 11587 | // Force a system caller here so that the window watcher won't screw us |
michael@0 | 11588 | // up. We do NOT want this case looking at the JS context on the stack |
michael@0 | 11589 | // when searching. Compare comments on |
michael@0 | 11590 | // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow. |
michael@0 | 11591 | |
michael@0 | 11592 | // Note: Because nsWindowWatcher is so broken, it's actually important |
michael@0 | 11593 | // that we don't force a system caller here, because that screws it up |
michael@0 | 11594 | // when it tries to compute the caller principal to associate with dialog |
michael@0 | 11595 | // arguments. That whole setup just really needs to be rewritten. :-( |
michael@0 | 11596 | Maybe<AutoNoJSAPI> nojsapi; |
michael@0 | 11597 | if (!aContentModal) { |
michael@0 | 11598 | nojsapi.construct(); |
michael@0 | 11599 | } |
michael@0 | 11600 | |
michael@0 | 11601 | |
michael@0 | 11602 | rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr, |
michael@0 | 11603 | /* aCalledFromScript = */ false, |
michael@0 | 11604 | aDialog, aNavigate, aExtraArgument, |
michael@0 | 11605 | getter_AddRefs(domReturn)); |
michael@0 | 11606 | |
michael@0 | 11607 | } |
michael@0 | 11608 | } |
michael@0 | 11609 | |
michael@0 | 11610 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 11611 | |
michael@0 | 11612 | // success! |
michael@0 | 11613 | |
michael@0 | 11614 | NS_ENSURE_TRUE(domReturn, NS_OK); |
michael@0 | 11615 | domReturn.swap(*aReturn); |
michael@0 | 11616 | |
michael@0 | 11617 | if (aDoJSFixups) { |
michael@0 | 11618 | nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn)); |
michael@0 | 11619 | if (!chrome_win) { |
michael@0 | 11620 | // A new non-chrome window was created from a call to |
michael@0 | 11621 | // window.open() from JavaScript, make sure there's a document in |
michael@0 | 11622 | // the new window. We do this by simply asking the new window for |
michael@0 | 11623 | // its document, this will synchronously create an empty document |
michael@0 | 11624 | // if there is no document in the window. |
michael@0 | 11625 | // XXXbz should this just use EnsureInnerWindow()? |
michael@0 | 11626 | #ifdef DEBUG_jst |
michael@0 | 11627 | { |
michael@0 | 11628 | nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*aReturn)); |
michael@0 | 11629 | NS_ASSERTION(pidomwin->GetExtantDoc(), "No document in new window!!!"); |
michael@0 | 11630 | } |
michael@0 | 11631 | #endif |
michael@0 | 11632 | |
michael@0 | 11633 | nsCOMPtr<nsIDOMDocument> doc; |
michael@0 | 11634 | (*aReturn)->GetDocument(getter_AddRefs(doc)); |
michael@0 | 11635 | } |
michael@0 | 11636 | } |
michael@0 | 11637 | |
michael@0 | 11638 | if (checkForPopup) { |
michael@0 | 11639 | if (abuseLevel >= openControlled) { |
michael@0 | 11640 | nsGlobalWindow *opened = static_cast<nsGlobalWindow *>(*aReturn); |
michael@0 | 11641 | if (!opened->IsPopupSpamWindow()) { |
michael@0 | 11642 | opened->SetPopupSpamWindow(true); |
michael@0 | 11643 | ++gOpenPopupSpamCount; |
michael@0 | 11644 | } |
michael@0 | 11645 | } |
michael@0 | 11646 | if (abuseLevel >= openAbused) |
michael@0 | 11647 | FireAbuseEvents(false, true, aUrl, aName, aOptions); |
michael@0 | 11648 | } |
michael@0 | 11649 | |
michael@0 | 11650 | return rv; |
michael@0 | 11651 | } |
michael@0 | 11652 | |
michael@0 | 11653 | //***************************************************************************** |
michael@0 | 11654 | // nsGlobalWindow: Timeout Functions |
michael@0 | 11655 | //***************************************************************************** |
michael@0 | 11656 | |
michael@0 | 11657 | uint32_t sNestingLevel; |
michael@0 | 11658 | |
michael@0 | 11659 | nsGlobalWindow* |
michael@0 | 11660 | nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError) |
michael@0 | 11661 | { |
michael@0 | 11662 | nsGlobalWindow* currentInner; |
michael@0 | 11663 | nsGlobalWindow* forwardTo; |
michael@0 | 11664 | if (IsInnerWindow()) { |
michael@0 | 11665 | nsGlobalWindow* outer = GetOuterWindowInternal(); |
michael@0 | 11666 | currentInner = outer ? outer->GetCurrentInnerWindowInternal() : this; |
michael@0 | 11667 | |
michael@0 | 11668 | forwardTo = this; |
michael@0 | 11669 | } else { |
michael@0 | 11670 | currentInner = GetCurrentInnerWindowInternal(); |
michael@0 | 11671 | |
michael@0 | 11672 | // This needs to forward to the inner window, but since the current |
michael@0 | 11673 | // inner may not be the inner in the calling scope, we need to treat |
michael@0 | 11674 | // this specially here as we don't want timeouts registered in a |
michael@0 | 11675 | // dying inner window to get registered and run on the current inner |
michael@0 | 11676 | // window. To get this right, we need to forward this call to the |
michael@0 | 11677 | // inner window that's calling window.setTimeout(). |
michael@0 | 11678 | |
michael@0 | 11679 | forwardTo = CallerInnerWindow(); |
michael@0 | 11680 | if (!forwardTo) { |
michael@0 | 11681 | aError.Throw(NS_ERROR_NOT_AVAILABLE); |
michael@0 | 11682 | return nullptr; |
michael@0 | 11683 | } |
michael@0 | 11684 | |
michael@0 | 11685 | // If the caller and the callee share the same outer window, forward to the |
michael@0 | 11686 | // caller inner. Else, we forward to the current inner (e.g. someone is |
michael@0 | 11687 | // calling setTimeout() on a reference to some other window). |
michael@0 | 11688 | if (forwardTo->GetOuterWindow() != this || !forwardTo->IsInnerWindow()) { |
michael@0 | 11689 | if (!currentInner) { |
michael@0 | 11690 | NS_WARNING("No inner window available!"); |
michael@0 | 11691 | aError.Throw(NS_ERROR_NOT_INITIALIZED); |
michael@0 | 11692 | return nullptr; |
michael@0 | 11693 | } |
michael@0 | 11694 | |
michael@0 | 11695 | return currentInner; |
michael@0 | 11696 | } |
michael@0 | 11697 | } |
michael@0 | 11698 | |
michael@0 | 11699 | // If forwardTo is not the window with an active document then we want the |
michael@0 | 11700 | // call to setTimeout/Interval to be a noop, so return null but don't set an |
michael@0 | 11701 | // error. |
michael@0 | 11702 | return forwardTo->HasActiveDocument() ? currentInner : nullptr; |
michael@0 | 11703 | } |
michael@0 | 11704 | |
michael@0 | 11705 | int32_t |
michael@0 | 11706 | nsGlobalWindow::SetTimeout(JSContext* aCx, Function& aFunction, |
michael@0 | 11707 | int32_t aTimeout, |
michael@0 | 11708 | const Sequence<JS::Value>& aArguments, |
michael@0 | 11709 | ErrorResult& aError) |
michael@0 | 11710 | { |
michael@0 | 11711 | return SetTimeoutOrInterval(aFunction, aTimeout, aArguments, false, aError); |
michael@0 | 11712 | } |
michael@0 | 11713 | |
michael@0 | 11714 | int32_t |
michael@0 | 11715 | nsGlobalWindow::SetTimeout(JSContext* aCx, const nsAString& aHandler, |
michael@0 | 11716 | int32_t aTimeout, |
michael@0 | 11717 | const Sequence<JS::Value>& /* unused */, |
michael@0 | 11718 | ErrorResult& aError) |
michael@0 | 11719 | { |
michael@0 | 11720 | return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError); |
michael@0 | 11721 | } |
michael@0 | 11722 | |
michael@0 | 11723 | static bool |
michael@0 | 11724 | IsInterval(const Optional<int32_t>& aTimeout, int32_t& aResultTimeout) |
michael@0 | 11725 | { |
michael@0 | 11726 | if (aTimeout.WasPassed()) { |
michael@0 | 11727 | aResultTimeout = aTimeout.Value(); |
michael@0 | 11728 | return true; |
michael@0 | 11729 | } |
michael@0 | 11730 | |
michael@0 | 11731 | // If no interval was specified, treat this like a timeout, to avoid setting |
michael@0 | 11732 | // an interval of 0 milliseconds. |
michael@0 | 11733 | aResultTimeout = 0; |
michael@0 | 11734 | return false; |
michael@0 | 11735 | } |
michael@0 | 11736 | |
michael@0 | 11737 | int32_t |
michael@0 | 11738 | nsGlobalWindow::SetInterval(JSContext* aCx, Function& aFunction, |
michael@0 | 11739 | const Optional<int32_t>& aTimeout, |
michael@0 | 11740 | const Sequence<JS::Value>& aArguments, |
michael@0 | 11741 | ErrorResult& aError) |
michael@0 | 11742 | { |
michael@0 | 11743 | int32_t timeout; |
michael@0 | 11744 | bool isInterval = IsInterval(aTimeout, timeout); |
michael@0 | 11745 | return SetTimeoutOrInterval(aFunction, timeout, aArguments, isInterval, |
michael@0 | 11746 | aError); |
michael@0 | 11747 | } |
michael@0 | 11748 | |
michael@0 | 11749 | int32_t |
michael@0 | 11750 | nsGlobalWindow::SetInterval(JSContext* aCx, const nsAString& aHandler, |
michael@0 | 11751 | const Optional<int32_t>& aTimeout, |
michael@0 | 11752 | const Sequence<JS::Value>& /* unused */, |
michael@0 | 11753 | ErrorResult& aError) |
michael@0 | 11754 | { |
michael@0 | 11755 | int32_t timeout; |
michael@0 | 11756 | bool isInterval = IsInterval(aTimeout, timeout); |
michael@0 | 11757 | return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError); |
michael@0 | 11758 | } |
michael@0 | 11759 | |
michael@0 | 11760 | nsresult |
michael@0 | 11761 | nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, |
michael@0 | 11762 | int32_t interval, |
michael@0 | 11763 | bool aIsInterval, int32_t *aReturn) |
michael@0 | 11764 | { |
michael@0 | 11765 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 11766 | |
michael@0 | 11767 | // If we don't have a document (we could have been unloaded since |
michael@0 | 11768 | // the call to setTimeout was made), do nothing. |
michael@0 | 11769 | if (!mDoc) { |
michael@0 | 11770 | return NS_OK; |
michael@0 | 11771 | } |
michael@0 | 11772 | |
michael@0 | 11773 | // Disallow negative intervals. If aIsInterval also disallow 0, |
michael@0 | 11774 | // because we use that as a "don't repeat" flag. |
michael@0 | 11775 | interval = std::max(aIsInterval ? 1 : 0, interval); |
michael@0 | 11776 | |
michael@0 | 11777 | // Make sure we don't proceed with an interval larger than our timer |
michael@0 | 11778 | // code can handle. (Note: we already forced |interval| to be non-negative, |
michael@0 | 11779 | // so the uint32_t cast (to avoid compiler warnings) is ok.) |
michael@0 | 11780 | uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE); |
michael@0 | 11781 | if (static_cast<uint32_t>(interval) > maxTimeoutMs) { |
michael@0 | 11782 | interval = maxTimeoutMs; |
michael@0 | 11783 | } |
michael@0 | 11784 | |
michael@0 | 11785 | nsRefPtr<nsTimeout> timeout = new nsTimeout(); |
michael@0 | 11786 | timeout->mIsInterval = aIsInterval; |
michael@0 | 11787 | timeout->mInterval = interval; |
michael@0 | 11788 | timeout->mScriptHandler = aHandler; |
michael@0 | 11789 | |
michael@0 | 11790 | // Now clamp the actual interval we will use for the timer based on |
michael@0 | 11791 | uint32_t nestingLevel = sNestingLevel + 1; |
michael@0 | 11792 | uint32_t realInterval = interval; |
michael@0 | 11793 | if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) { |
michael@0 | 11794 | // Don't allow timeouts less than DOMMinTimeoutValue() from |
michael@0 | 11795 | // now... |
michael@0 | 11796 | realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue())); |
michael@0 | 11797 | } |
michael@0 | 11798 | |
michael@0 | 11799 | // Get principal of currently executing code, save for execution of timeout. |
michael@0 | 11800 | // If our principals subsume the subject principal then use the subject |
michael@0 | 11801 | // principal. Otherwise, use our principal to avoid running script in |
michael@0 | 11802 | // elevated principals. |
michael@0 | 11803 | |
michael@0 | 11804 | nsCOMPtr<nsIPrincipal> subjectPrincipal; |
michael@0 | 11805 | nsresult rv; |
michael@0 | 11806 | rv = nsContentUtils::GetSecurityManager()-> |
michael@0 | 11807 | GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); |
michael@0 | 11808 | if (NS_FAILED(rv)) { |
michael@0 | 11809 | return NS_ERROR_FAILURE; |
michael@0 | 11810 | } |
michael@0 | 11811 | |
michael@0 | 11812 | bool subsumes = false; |
michael@0 | 11813 | nsCOMPtr<nsIPrincipal> ourPrincipal = GetPrincipal(); |
michael@0 | 11814 | |
michael@0 | 11815 | // Note the direction of this test: We don't allow setTimeouts running with |
michael@0 | 11816 | // chrome privileges on content windows, but we do allow setTimeouts running |
michael@0 | 11817 | // with content privileges on chrome windows (where they can't do very much, |
michael@0 | 11818 | // of course). |
michael@0 | 11819 | rv = ourPrincipal->Subsumes(subjectPrincipal, &subsumes); |
michael@0 | 11820 | if (NS_FAILED(rv)) { |
michael@0 | 11821 | return NS_ERROR_FAILURE; |
michael@0 | 11822 | } |
michael@0 | 11823 | |
michael@0 | 11824 | if (subsumes) { |
michael@0 | 11825 | timeout->mPrincipal = subjectPrincipal; |
michael@0 | 11826 | } else { |
michael@0 | 11827 | timeout->mPrincipal = ourPrincipal; |
michael@0 | 11828 | } |
michael@0 | 11829 | |
michael@0 | 11830 | ++gTimeoutsRecentlySet; |
michael@0 | 11831 | TimeDuration delta = TimeDuration::FromMilliseconds(realInterval); |
michael@0 | 11832 | |
michael@0 | 11833 | if (!IsFrozen() && !mTimeoutsSuspendDepth) { |
michael@0 | 11834 | // If we're not currently frozen, then we set timeout->mWhen to be the |
michael@0 | 11835 | // actual firing time of the timer (i.e., now + delta). We also actually |
michael@0 | 11836 | // create a timer and fire it off. |
michael@0 | 11837 | |
michael@0 | 11838 | timeout->mWhen = TimeStamp::Now() + delta; |
michael@0 | 11839 | |
michael@0 | 11840 | timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); |
michael@0 | 11841 | if (NS_FAILED(rv)) { |
michael@0 | 11842 | return rv; |
michael@0 | 11843 | } |
michael@0 | 11844 | |
michael@0 | 11845 | nsRefPtr<nsTimeout> copy = timeout; |
michael@0 | 11846 | |
michael@0 | 11847 | rv = timeout->InitTimer(TimerCallback, realInterval); |
michael@0 | 11848 | if (NS_FAILED(rv)) { |
michael@0 | 11849 | return rv; |
michael@0 | 11850 | } |
michael@0 | 11851 | |
michael@0 | 11852 | // The timeout is now also held in the timer's closure. |
michael@0 | 11853 | unused << copy.forget(); |
michael@0 | 11854 | } else { |
michael@0 | 11855 | // If we are frozen, however, then we instead simply set |
michael@0 | 11856 | // timeout->mTimeRemaining to be the "time remaining" in the timeout (i.e., |
michael@0 | 11857 | // the interval itself). We don't create a timer for it, since that will |
michael@0 | 11858 | // happen when we are thawed and the timeout will then get a timer and run |
michael@0 | 11859 | // to completion. |
michael@0 | 11860 | |
michael@0 | 11861 | timeout->mTimeRemaining = delta; |
michael@0 | 11862 | } |
michael@0 | 11863 | |
michael@0 | 11864 | timeout->mWindow = this; |
michael@0 | 11865 | |
michael@0 | 11866 | if (!aIsInterval) { |
michael@0 | 11867 | timeout->mNestingLevel = nestingLevel; |
michael@0 | 11868 | } |
michael@0 | 11869 | |
michael@0 | 11870 | // No popups from timeouts by default |
michael@0 | 11871 | timeout->mPopupState = openAbused; |
michael@0 | 11872 | |
michael@0 | 11873 | if (gRunningTimeoutDepth == 0 && gPopupControlState < openAbused) { |
michael@0 | 11874 | // This timeout is *not* set from another timeout and it's set |
michael@0 | 11875 | // while popups are enabled. Propagate the state to the timeout if |
michael@0 | 11876 | // its delay (interval) is equal to or less than what |
michael@0 | 11877 | // "dom.disable_open_click_delay" is set to (in ms). |
michael@0 | 11878 | |
michael@0 | 11879 | int32_t delay = |
michael@0 | 11880 | Preferences::GetInt("dom.disable_open_click_delay"); |
michael@0 | 11881 | |
michael@0 | 11882 | // This is checking |interval|, not realInterval, on purpose, |
michael@0 | 11883 | // because our lower bound for |realInterval| could be pretty high |
michael@0 | 11884 | // in some cases. |
michael@0 | 11885 | if (interval <= delay) { |
michael@0 | 11886 | timeout->mPopupState = gPopupControlState; |
michael@0 | 11887 | } |
michael@0 | 11888 | } |
michael@0 | 11889 | |
michael@0 | 11890 | InsertTimeoutIntoList(timeout); |
michael@0 | 11891 | |
michael@0 | 11892 | timeout->mPublicId = ++mTimeoutPublicIdCounter; |
michael@0 | 11893 | *aReturn = timeout->mPublicId; |
michael@0 | 11894 | |
michael@0 | 11895 | return NS_OK; |
michael@0 | 11896 | |
michael@0 | 11897 | } |
michael@0 | 11898 | |
michael@0 | 11899 | nsresult |
michael@0 | 11900 | nsGlobalWindow::SetTimeoutOrInterval(bool aIsInterval, int32_t *aReturn) |
michael@0 | 11901 | { |
michael@0 | 11902 | // This needs to forward to the inner window, but since the current |
michael@0 | 11903 | // inner may not be the inner in the calling scope, we need to treat |
michael@0 | 11904 | // this specially here as we don't want timeouts registered in a |
michael@0 | 11905 | // dying inner window to get registered and run on the current inner |
michael@0 | 11906 | // window. To get this right, we need to forward this call to the |
michael@0 | 11907 | // inner window that's calling window.setTimeout(). |
michael@0 | 11908 | |
michael@0 | 11909 | if (IsOuterWindow()) { |
michael@0 | 11910 | nsGlobalWindow* callerInner = CallerInnerWindow(); |
michael@0 | 11911 | NS_ENSURE_TRUE(callerInner, NS_ERROR_NOT_AVAILABLE); |
michael@0 | 11912 | |
michael@0 | 11913 | // If the caller and the callee share the same outer window, |
michael@0 | 11914 | // forward to the callee inner. Else, we forward to the current |
michael@0 | 11915 | // inner (e.g. someone is calling setTimeout() on a reference to |
michael@0 | 11916 | // some other window). |
michael@0 | 11917 | |
michael@0 | 11918 | if (callerInner->GetOuterWindow() == this && |
michael@0 | 11919 | callerInner->IsInnerWindow()) { |
michael@0 | 11920 | return callerInner->SetTimeoutOrInterval(aIsInterval, aReturn); |
michael@0 | 11921 | } |
michael@0 | 11922 | |
michael@0 | 11923 | FORWARD_TO_INNER(SetTimeoutOrInterval, (aIsInterval, aReturn), |
michael@0 | 11924 | NS_ERROR_NOT_INITIALIZED); |
michael@0 | 11925 | } |
michael@0 | 11926 | |
michael@0 | 11927 | int32_t interval = 0; |
michael@0 | 11928 | bool isInterval = aIsInterval; |
michael@0 | 11929 | nsCOMPtr<nsIScriptTimeoutHandler> handler; |
michael@0 | 11930 | nsresult rv = NS_CreateJSTimeoutHandler(this, |
michael@0 | 11931 | &isInterval, |
michael@0 | 11932 | &interval, |
michael@0 | 11933 | getter_AddRefs(handler)); |
michael@0 | 11934 | if (!handler) { |
michael@0 | 11935 | *aReturn = 0; |
michael@0 | 11936 | return rv; |
michael@0 | 11937 | } |
michael@0 | 11938 | |
michael@0 | 11939 | return SetTimeoutOrInterval(handler, interval, isInterval, aReturn); |
michael@0 | 11940 | } |
michael@0 | 11941 | |
michael@0 | 11942 | int32_t |
michael@0 | 11943 | nsGlobalWindow::SetTimeoutOrInterval(Function& aFunction, int32_t aTimeout, |
michael@0 | 11944 | const Sequence<JS::Value>& aArguments, |
michael@0 | 11945 | bool aIsInterval, ErrorResult& aError) |
michael@0 | 11946 | { |
michael@0 | 11947 | nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError); |
michael@0 | 11948 | if (!inner) { |
michael@0 | 11949 | return -1; |
michael@0 | 11950 | } |
michael@0 | 11951 | |
michael@0 | 11952 | if (inner != this) { |
michael@0 | 11953 | return inner->SetTimeoutOrInterval(aFunction, aTimeout, aArguments, |
michael@0 | 11954 | aIsInterval, aError); |
michael@0 | 11955 | } |
michael@0 | 11956 | |
michael@0 | 11957 | nsCOMPtr<nsIScriptTimeoutHandler> handler = |
michael@0 | 11958 | NS_CreateJSTimeoutHandler(this, aFunction, aArguments, aError); |
michael@0 | 11959 | if (!handler) { |
michael@0 | 11960 | return 0; |
michael@0 | 11961 | } |
michael@0 | 11962 | |
michael@0 | 11963 | int32_t result; |
michael@0 | 11964 | aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result); |
michael@0 | 11965 | return result; |
michael@0 | 11966 | } |
michael@0 | 11967 | |
michael@0 | 11968 | int32_t |
michael@0 | 11969 | nsGlobalWindow::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler, |
michael@0 | 11970 | int32_t aTimeout, bool aIsInterval, |
michael@0 | 11971 | ErrorResult& aError) |
michael@0 | 11972 | { |
michael@0 | 11973 | nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError); |
michael@0 | 11974 | if (!inner) { |
michael@0 | 11975 | return -1; |
michael@0 | 11976 | } |
michael@0 | 11977 | |
michael@0 | 11978 | if (inner != this) { |
michael@0 | 11979 | return inner->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval, |
michael@0 | 11980 | aError); |
michael@0 | 11981 | } |
michael@0 | 11982 | |
michael@0 | 11983 | nsCOMPtr<nsIScriptTimeoutHandler> handler = |
michael@0 | 11984 | NS_CreateJSTimeoutHandler(aCx, this, aHandler, aError); |
michael@0 | 11985 | if (!handler) { |
michael@0 | 11986 | return 0; |
michael@0 | 11987 | } |
michael@0 | 11988 | |
michael@0 | 11989 | int32_t result; |
michael@0 | 11990 | aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval, &result); |
michael@0 | 11991 | return result; |
michael@0 | 11992 | } |
michael@0 | 11993 | |
michael@0 | 11994 | bool |
michael@0 | 11995 | nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout, |
michael@0 | 11996 | nsIScriptContext* aScx) |
michael@0 | 11997 | { |
michael@0 | 11998 | // Hold on to the timeout in case mExpr or mFunObj releases its |
michael@0 | 11999 | // doc. |
michael@0 | 12000 | nsRefPtr<nsTimeout> timeout = aTimeout; |
michael@0 | 12001 | nsTimeout* last_running_timeout = mRunningTimeout; |
michael@0 | 12002 | mRunningTimeout = timeout; |
michael@0 | 12003 | timeout->mRunning = true; |
michael@0 | 12004 | |
michael@0 | 12005 | // Push this timeout's popup control state, which should only be |
michael@0 | 12006 | // eabled the first time a timeout fires that was created while |
michael@0 | 12007 | // popups were enabled and with a delay less than |
michael@0 | 12008 | // "dom.disable_open_click_delay". |
michael@0 | 12009 | nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState); |
michael@0 | 12010 | |
michael@0 | 12011 | // Clear the timeout's popup state, if any, to prevent interval |
michael@0 | 12012 | // timeouts from repeatedly opening poups. |
michael@0 | 12013 | timeout->mPopupState = openAbused; |
michael@0 | 12014 | |
michael@0 | 12015 | ++gRunningTimeoutDepth; |
michael@0 | 12016 | ++mTimeoutFiringDepth; |
michael@0 | 12017 | |
michael@0 | 12018 | bool trackNestingLevel = !timeout->mIsInterval; |
michael@0 | 12019 | uint32_t nestingLevel; |
michael@0 | 12020 | if (trackNestingLevel) { |
michael@0 | 12021 | nestingLevel = sNestingLevel; |
michael@0 | 12022 | sNestingLevel = timeout->mNestingLevel; |
michael@0 | 12023 | } |
michael@0 | 12024 | |
michael@0 | 12025 | nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler); |
michael@0 | 12026 | nsRefPtr<Function> callback = handler->GetCallback(); |
michael@0 | 12027 | if (!callback) { |
michael@0 | 12028 | // Evaluate the timeout expression. |
michael@0 | 12029 | const char16_t* script = handler->GetHandlerText(); |
michael@0 | 12030 | NS_ASSERTION(script, "timeout has no script nor handler text!"); |
michael@0 | 12031 | |
michael@0 | 12032 | const char* filename = nullptr; |
michael@0 | 12033 | uint32_t lineNo = 0; |
michael@0 | 12034 | handler->GetLocation(&filename, &lineNo); |
michael@0 | 12035 | |
michael@0 | 12036 | // New script entry point required, due to the "Create a script" sub-step of |
michael@0 | 12037 | // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialization-steps |
michael@0 | 12038 | AutoEntryScript entryScript(this, true, aScx->GetNativeContext()); |
michael@0 | 12039 | JS::CompileOptions options(entryScript.cx()); |
michael@0 | 12040 | options.setFileAndLine(filename, lineNo) |
michael@0 | 12041 | .setVersion(JSVERSION_DEFAULT); |
michael@0 | 12042 | JS::Rooted<JSObject*> global(entryScript.cx(), FastGetGlobalJSObject()); |
michael@0 | 12043 | nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script), |
michael@0 | 12044 | global, options); |
michael@0 | 12045 | } else { |
michael@0 | 12046 | // Hold strong ref to ourselves while we call the callback. |
michael@0 | 12047 | nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this)); |
michael@0 | 12048 | ErrorResult ignored; |
michael@0 | 12049 | JS::Rooted<JS::Value> ignoredVal(CycleCollectedJSRuntime::Get()->Runtime()); |
michael@0 | 12050 | callback->Call(me, handler->GetArgs(), &ignoredVal, ignored); |
michael@0 | 12051 | } |
michael@0 | 12052 | |
michael@0 | 12053 | // We ignore any failures from calling EvaluateString() on the context or |
michael@0 | 12054 | // Call() on a Function here since we're in a loop |
michael@0 | 12055 | // where we're likely to be running timeouts whose OS timers |
michael@0 | 12056 | // didn't fire in time and we don't want to not fire those timers |
michael@0 | 12057 | // now just because execution of one timer failed. We can't |
michael@0 | 12058 | // propagate the error to anyone who cares about it from this |
michael@0 | 12059 | // point anyway, and the script context should have already reported |
michael@0 | 12060 | // the script error in the usual way - so we just drop it. |
michael@0 | 12061 | |
michael@0 | 12062 | if (trackNestingLevel) { |
michael@0 | 12063 | sNestingLevel = nestingLevel; |
michael@0 | 12064 | } |
michael@0 | 12065 | |
michael@0 | 12066 | --mTimeoutFiringDepth; |
michael@0 | 12067 | --gRunningTimeoutDepth; |
michael@0 | 12068 | |
michael@0 | 12069 | mRunningTimeout = last_running_timeout; |
michael@0 | 12070 | timeout->mRunning = false; |
michael@0 | 12071 | return timeout->mCleared; |
michael@0 | 12072 | } |
michael@0 | 12073 | |
michael@0 | 12074 | bool |
michael@0 | 12075 | nsGlobalWindow::RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now, |
michael@0 | 12076 | bool aRunningPendingTimeouts) |
michael@0 | 12077 | { |
michael@0 | 12078 | if (!aTimeout->mIsInterval) { |
michael@0 | 12079 | if (aTimeout->mTimer) { |
michael@0 | 12080 | // The timeout still has an OS timer, and it's not an interval, |
michael@0 | 12081 | // that means that the OS timer could still fire; cancel the OS |
michael@0 | 12082 | // timer and release its reference to the timeout. |
michael@0 | 12083 | aTimeout->mTimer->Cancel(); |
michael@0 | 12084 | aTimeout->mTimer = nullptr; |
michael@0 | 12085 | aTimeout->Release(); |
michael@0 | 12086 | } |
michael@0 | 12087 | return false; |
michael@0 | 12088 | } |
michael@0 | 12089 | |
michael@0 | 12090 | // Compute time to next timeout for interval timer. |
michael@0 | 12091 | // Make sure nextInterval is at least DOMMinTimeoutValue(). |
michael@0 | 12092 | TimeDuration nextInterval = |
michael@0 | 12093 | TimeDuration::FromMilliseconds(std::max(aTimeout->mInterval, |
michael@0 | 12094 | uint32_t(DOMMinTimeoutValue()))); |
michael@0 | 12095 | |
michael@0 | 12096 | // If we're running pending timeouts, set the next interval to be |
michael@0 | 12097 | // relative to "now", and not to when the timeout that was pending |
michael@0 | 12098 | // should have fired. |
michael@0 | 12099 | TimeStamp firingTime; |
michael@0 | 12100 | if (aRunningPendingTimeouts) { |
michael@0 | 12101 | firingTime = now + nextInterval; |
michael@0 | 12102 | } else { |
michael@0 | 12103 | firingTime = aTimeout->mWhen + nextInterval; |
michael@0 | 12104 | } |
michael@0 | 12105 | |
michael@0 | 12106 | TimeStamp currentNow = TimeStamp::Now(); |
michael@0 | 12107 | TimeDuration delay = firingTime - currentNow; |
michael@0 | 12108 | |
michael@0 | 12109 | // And make sure delay is nonnegative; that might happen if the timer |
michael@0 | 12110 | // thread is firing our timers somewhat early or if they're taking a long |
michael@0 | 12111 | // time to run the callback. |
michael@0 | 12112 | if (delay < TimeDuration(0)) { |
michael@0 | 12113 | delay = TimeDuration(0); |
michael@0 | 12114 | } |
michael@0 | 12115 | |
michael@0 | 12116 | if (!aTimeout->mTimer) { |
michael@0 | 12117 | NS_ASSERTION(IsFrozen() || mTimeoutsSuspendDepth, |
michael@0 | 12118 | "How'd our timer end up null if we're not frozen or " |
michael@0 | 12119 | "suspended?"); |
michael@0 | 12120 | |
michael@0 | 12121 | aTimeout->mTimeRemaining = delay; |
michael@0 | 12122 | return true; |
michael@0 | 12123 | } |
michael@0 | 12124 | |
michael@0 | 12125 | aTimeout->mWhen = currentNow + delay; |
michael@0 | 12126 | |
michael@0 | 12127 | // Reschedule the OS timer. Don't bother returning any error codes if |
michael@0 | 12128 | // this fails since the callers of this method don't care about them. |
michael@0 | 12129 | nsresult rv = aTimeout->InitTimer(TimerCallback, delay.ToMilliseconds()); |
michael@0 | 12130 | |
michael@0 | 12131 | if (NS_FAILED(rv)) { |
michael@0 | 12132 | NS_ERROR("Error initializing timer for DOM timeout!"); |
michael@0 | 12133 | |
michael@0 | 12134 | // We failed to initialize the new OS timer, this timer does |
michael@0 | 12135 | // us no good here so we just cancel it (just in case) and |
michael@0 | 12136 | // null out the pointer to the OS timer, this will release the |
michael@0 | 12137 | // OS timer. As we continue executing the code below we'll end |
michael@0 | 12138 | // up deleting the timeout since it's not an interval timeout |
michael@0 | 12139 | // any more (since timeout->mTimer == nullptr). |
michael@0 | 12140 | aTimeout->mTimer->Cancel(); |
michael@0 | 12141 | aTimeout->mTimer = nullptr; |
michael@0 | 12142 | |
michael@0 | 12143 | // Now that the OS timer no longer has a reference to the |
michael@0 | 12144 | // timeout we need to drop that reference. |
michael@0 | 12145 | aTimeout->Release(); |
michael@0 | 12146 | |
michael@0 | 12147 | return false; |
michael@0 | 12148 | } |
michael@0 | 12149 | |
michael@0 | 12150 | return true; |
michael@0 | 12151 | } |
michael@0 | 12152 | |
michael@0 | 12153 | void |
michael@0 | 12154 | nsGlobalWindow::RunTimeout(nsTimeout *aTimeout) |
michael@0 | 12155 | { |
michael@0 | 12156 | // If a modal dialog is open for this window, return early. Pending |
michael@0 | 12157 | // timeouts will run when the modal dialog is dismissed. |
michael@0 | 12158 | if (IsInModalState() || mTimeoutsSuspendDepth) { |
michael@0 | 12159 | return; |
michael@0 | 12160 | } |
michael@0 | 12161 | |
michael@0 | 12162 | NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!"); |
michael@0 | 12163 | NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!"); |
michael@0 | 12164 | |
michael@0 | 12165 | nsTimeout *nextTimeout; |
michael@0 | 12166 | nsTimeout *last_expired_timeout, *last_insertion_point; |
michael@0 | 12167 | uint32_t firingDepth = mTimeoutFiringDepth + 1; |
michael@0 | 12168 | |
michael@0 | 12169 | // Make sure that the window and the script context don't go away as |
michael@0 | 12170 | // a result of running timeouts |
michael@0 | 12171 | nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(this); |
michael@0 | 12172 | |
michael@0 | 12173 | // A native timer has gone off. See which of our timeouts need |
michael@0 | 12174 | // servicing |
michael@0 | 12175 | TimeStamp now = TimeStamp::Now(); |
michael@0 | 12176 | TimeStamp deadline; |
michael@0 | 12177 | |
michael@0 | 12178 | if (aTimeout && aTimeout->mWhen > now) { |
michael@0 | 12179 | // The OS timer fired early (which can happen due to the timers |
michael@0 | 12180 | // having lower precision than TimeStamp does). Set |deadline| to |
michael@0 | 12181 | // be the time when the OS timer *should* have fired so that any |
michael@0 | 12182 | // timers that *should* have fired before aTimeout *will* be fired |
michael@0 | 12183 | // now. |
michael@0 | 12184 | |
michael@0 | 12185 | deadline = aTimeout->mWhen; |
michael@0 | 12186 | } else { |
michael@0 | 12187 | deadline = now; |
michael@0 | 12188 | } |
michael@0 | 12189 | |
michael@0 | 12190 | // The timeout list is kept in deadline order. Discover the latest |
michael@0 | 12191 | // timeout whose deadline has expired. On some platforms, native |
michael@0 | 12192 | // timeout events fire "early", so we need to test the timer as well |
michael@0 | 12193 | // as the deadline. |
michael@0 | 12194 | last_expired_timeout = nullptr; |
michael@0 | 12195 | for (nsTimeout *timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) { |
michael@0 | 12196 | if (((timeout == aTimeout) || (timeout->mWhen <= deadline)) && |
michael@0 | 12197 | (timeout->mFiringDepth == 0)) { |
michael@0 | 12198 | // Mark any timeouts that are on the list to be fired with the |
michael@0 | 12199 | // firing depth so that we can reentrantly run timeouts |
michael@0 | 12200 | timeout->mFiringDepth = firingDepth; |
michael@0 | 12201 | last_expired_timeout = timeout; |
michael@0 | 12202 | } |
michael@0 | 12203 | } |
michael@0 | 12204 | |
michael@0 | 12205 | // Maybe the timeout that the event was fired for has been deleted |
michael@0 | 12206 | // and there are no others timeouts with deadlines that make them |
michael@0 | 12207 | // eligible for execution yet. Go away. |
michael@0 | 12208 | if (!last_expired_timeout) { |
michael@0 | 12209 | return; |
michael@0 | 12210 | } |
michael@0 | 12211 | |
michael@0 | 12212 | // Record telemetry information about timers set recently. |
michael@0 | 12213 | TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL); |
michael@0 | 12214 | if (gLastRecordedRecentTimeouts.IsNull() || |
michael@0 | 12215 | now - gLastRecordedRecentTimeouts > recordingInterval) { |
michael@0 | 12216 | uint32_t count = gTimeoutsRecentlySet; |
michael@0 | 12217 | gTimeoutsRecentlySet = 0; |
michael@0 | 12218 | Telemetry::Accumulate(Telemetry::DOM_TIMERS_RECENTLY_SET, count); |
michael@0 | 12219 | gLastRecordedRecentTimeouts = now; |
michael@0 | 12220 | } |
michael@0 | 12221 | |
michael@0 | 12222 | // Insert a dummy timeout into the list of timeouts between the |
michael@0 | 12223 | // portion of the list that we are about to process now and those |
michael@0 | 12224 | // timeouts that will be processed in a future call to |
michael@0 | 12225 | // win_run_timeout(). This dummy timeout serves as the head of the |
michael@0 | 12226 | // list for any timeouts inserted as a result of running a timeout. |
michael@0 | 12227 | nsRefPtr<nsTimeout> dummy_timeout = new nsTimeout(); |
michael@0 | 12228 | dummy_timeout->mFiringDepth = firingDepth; |
michael@0 | 12229 | dummy_timeout->mWhen = now; |
michael@0 | 12230 | last_expired_timeout->setNext(dummy_timeout); |
michael@0 | 12231 | dummy_timeout->AddRef(); |
michael@0 | 12232 | |
michael@0 | 12233 | last_insertion_point = mTimeoutInsertionPoint; |
michael@0 | 12234 | // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout, |
michael@0 | 12235 | // the logic in ResetTimersForNonBackgroundWindow will need to change. |
michael@0 | 12236 | mTimeoutInsertionPoint = dummy_timeout; |
michael@0 | 12237 | |
michael@0 | 12238 | Telemetry::AutoCounter<Telemetry::DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT> timeoutsRan; |
michael@0 | 12239 | |
michael@0 | 12240 | for (nsTimeout *timeout = mTimeouts.getFirst(); |
michael@0 | 12241 | timeout != dummy_timeout && !IsFrozen(); |
michael@0 | 12242 | timeout = nextTimeout) { |
michael@0 | 12243 | nextTimeout = timeout->getNext(); |
michael@0 | 12244 | |
michael@0 | 12245 | if (timeout->mFiringDepth != firingDepth) { |
michael@0 | 12246 | // We skip the timeout since it's on the list to run at another |
michael@0 | 12247 | // depth. |
michael@0 | 12248 | |
michael@0 | 12249 | continue; |
michael@0 | 12250 | } |
michael@0 | 12251 | |
michael@0 | 12252 | if (mTimeoutsSuspendDepth) { |
michael@0 | 12253 | // Some timer did suspend us. Make sure the |
michael@0 | 12254 | // rest of the timers get executed later. |
michael@0 | 12255 | timeout->mFiringDepth = 0; |
michael@0 | 12256 | continue; |
michael@0 | 12257 | } |
michael@0 | 12258 | |
michael@0 | 12259 | // The timeout is on the list to run at this depth, go ahead and |
michael@0 | 12260 | // process it. |
michael@0 | 12261 | |
michael@0 | 12262 | // Get the script context (a strong ref to prevent it going away) |
michael@0 | 12263 | // for this timeout and ensure the script language is enabled. |
michael@0 | 12264 | nsCOMPtr<nsIScriptContext> scx = GetContextInternal(); |
michael@0 | 12265 | |
michael@0 | 12266 | if (!scx) { |
michael@0 | 12267 | // No context means this window was closed or never properly |
michael@0 | 12268 | // initialized for this language. |
michael@0 | 12269 | continue; |
michael@0 | 12270 | } |
michael@0 | 12271 | |
michael@0 | 12272 | // This timeout is good to run |
michael@0 | 12273 | ++timeoutsRan; |
michael@0 | 12274 | bool timeout_was_cleared = RunTimeoutHandler(timeout, scx); |
michael@0 | 12275 | |
michael@0 | 12276 | if (timeout_was_cleared) { |
michael@0 | 12277 | // The running timeout's window was cleared, this means that |
michael@0 | 12278 | // ClearAllTimeouts() was called from a *nested* call, possibly |
michael@0 | 12279 | // through a timeout that fired while a modal (to this window) |
michael@0 | 12280 | // dialog was open or through other non-obvious paths. |
michael@0 | 12281 | MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak"); |
michael@0 | 12282 | |
michael@0 | 12283 | mTimeoutInsertionPoint = last_insertion_point; |
michael@0 | 12284 | |
michael@0 | 12285 | return; |
michael@0 | 12286 | } |
michael@0 | 12287 | |
michael@0 | 12288 | // If we have a regular interval timer, we re-schedule the |
michael@0 | 12289 | // timeout, accounting for clock drift. |
michael@0 | 12290 | bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout); |
michael@0 | 12291 | |
michael@0 | 12292 | // Running a timeout can cause another timeout to be deleted, so |
michael@0 | 12293 | // we need to reset the pointer to the following timeout. |
michael@0 | 12294 | nextTimeout = timeout->getNext(); |
michael@0 | 12295 | |
michael@0 | 12296 | timeout->remove(); |
michael@0 | 12297 | |
michael@0 | 12298 | if (needsReinsertion) { |
michael@0 | 12299 | // Insert interval timeout onto list sorted in deadline order. |
michael@0 | 12300 | // AddRefs timeout. |
michael@0 | 12301 | InsertTimeoutIntoList(timeout); |
michael@0 | 12302 | } |
michael@0 | 12303 | |
michael@0 | 12304 | // Release the timeout struct since it's possibly out of the list |
michael@0 | 12305 | timeout->Release(); |
michael@0 | 12306 | } |
michael@0 | 12307 | |
michael@0 | 12308 | // Take the dummy timeout off the head of the list |
michael@0 | 12309 | dummy_timeout->remove(); |
michael@0 | 12310 | dummy_timeout->Release(); |
michael@0 | 12311 | MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak"); |
michael@0 | 12312 | |
michael@0 | 12313 | mTimeoutInsertionPoint = last_insertion_point; |
michael@0 | 12314 | } |
michael@0 | 12315 | |
michael@0 | 12316 | void |
michael@0 | 12317 | nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerID, ErrorResult& aError) |
michael@0 | 12318 | { |
michael@0 | 12319 | FORWARD_TO_INNER_OR_THROW(ClearTimeoutOrInterval, (aTimerID, aError), |
michael@0 | 12320 | aError, ); |
michael@0 | 12321 | |
michael@0 | 12322 | uint32_t public_id = (uint32_t)aTimerID; |
michael@0 | 12323 | nsTimeout *timeout; |
michael@0 | 12324 | |
michael@0 | 12325 | for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) { |
michael@0 | 12326 | if (timeout->mPublicId == public_id) { |
michael@0 | 12327 | if (timeout->mRunning) { |
michael@0 | 12328 | /* We're running from inside the timeout. Mark this |
michael@0 | 12329 | timeout for deferred deletion by the code in |
michael@0 | 12330 | RunTimeout() */ |
michael@0 | 12331 | timeout->mIsInterval = false; |
michael@0 | 12332 | } |
michael@0 | 12333 | else { |
michael@0 | 12334 | /* Delete the timeout from the pending timeout list */ |
michael@0 | 12335 | timeout->remove(); |
michael@0 | 12336 | |
michael@0 | 12337 | if (timeout->mTimer) { |
michael@0 | 12338 | timeout->mTimer->Cancel(); |
michael@0 | 12339 | timeout->mTimer = nullptr; |
michael@0 | 12340 | timeout->Release(); |
michael@0 | 12341 | } |
michael@0 | 12342 | timeout->Release(); |
michael@0 | 12343 | } |
michael@0 | 12344 | break; |
michael@0 | 12345 | } |
michael@0 | 12346 | } |
michael@0 | 12347 | } |
michael@0 | 12348 | |
michael@0 | 12349 | nsresult nsGlobalWindow::ResetTimersForNonBackgroundWindow() |
michael@0 | 12350 | { |
michael@0 | 12351 | FORWARD_TO_INNER(ResetTimersForNonBackgroundWindow, (), |
michael@0 | 12352 | NS_ERROR_NOT_INITIALIZED); |
michael@0 | 12353 | |
michael@0 | 12354 | if (IsFrozen() || mTimeoutsSuspendDepth) { |
michael@0 | 12355 | return NS_OK; |
michael@0 | 12356 | } |
michael@0 | 12357 | |
michael@0 | 12358 | TimeStamp now = TimeStamp::Now(); |
michael@0 | 12359 | |
michael@0 | 12360 | // If mTimeoutInsertionPoint is non-null, we're in the middle of firing |
michael@0 | 12361 | // timers and the timers we're planning to fire all come before |
michael@0 | 12362 | // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout |
michael@0 | 12363 | // with an mWhen that may be semi-bogus. In that case, we don't need to do |
michael@0 | 12364 | // anything with mTimeoutInsertionPoint or anything before it, so should |
michael@0 | 12365 | // start at the timer after mTimeoutInsertionPoint, if there is one. |
michael@0 | 12366 | // Otherwise, start at the beginning of the list. |
michael@0 | 12367 | for (nsTimeout *timeout = mTimeoutInsertionPoint ? |
michael@0 | 12368 | mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst(); |
michael@0 | 12369 | timeout; ) { |
michael@0 | 12370 | // It's important that this check be <= so that we guarantee that |
michael@0 | 12371 | // taking std::max with |now| won't make a quantity equal to |
michael@0 | 12372 | // timeout->mWhen below. |
michael@0 | 12373 | if (timeout->mWhen <= now) { |
michael@0 | 12374 | timeout = timeout->getNext(); |
michael@0 | 12375 | continue; |
michael@0 | 12376 | } |
michael@0 | 12377 | |
michael@0 | 12378 | if (timeout->mWhen - now > |
michael@0 | 12379 | TimeDuration::FromMilliseconds(gMinBackgroundTimeoutValue)) { |
michael@0 | 12380 | // No need to loop further. Timeouts are sorted in mWhen order |
michael@0 | 12381 | // and the ones after this point were all set up for at least |
michael@0 | 12382 | // gMinBackgroundTimeoutValue ms and hence were not clamped. |
michael@0 | 12383 | break; |
michael@0 | 12384 | } |
michael@0 | 12385 | |
michael@0 | 12386 | /* We switched from background. Re-init the timer appropriately */ |
michael@0 | 12387 | // Compute the interval the timer should have had if it had not been set in a |
michael@0 | 12388 | // background window |
michael@0 | 12389 | TimeDuration interval = |
michael@0 | 12390 | TimeDuration::FromMilliseconds(std::max(timeout->mInterval, |
michael@0 | 12391 | uint32_t(DOMMinTimeoutValue()))); |
michael@0 | 12392 | uint32_t oldIntervalMillisecs = 0; |
michael@0 | 12393 | timeout->mTimer->GetDelay(&oldIntervalMillisecs); |
michael@0 | 12394 | TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs); |
michael@0 | 12395 | if (oldInterval > interval) { |
michael@0 | 12396 | // unclamp |
michael@0 | 12397 | TimeStamp firingTime = |
michael@0 | 12398 | std::max(timeout->mWhen - oldInterval + interval, now); |
michael@0 | 12399 | |
michael@0 | 12400 | NS_ASSERTION(firingTime < timeout->mWhen, |
michael@0 | 12401 | "Our firing time should strictly decrease!"); |
michael@0 | 12402 | |
michael@0 | 12403 | TimeDuration delay = firingTime - now; |
michael@0 | 12404 | timeout->mWhen = firingTime; |
michael@0 | 12405 | |
michael@0 | 12406 | // Since we reset mWhen we need to move |timeout| to the right |
michael@0 | 12407 | // place in the list so that it remains sorted by mWhen. |
michael@0 | 12408 | |
michael@0 | 12409 | // Get the pointer to the next timeout now, before we move the |
michael@0 | 12410 | // current timeout in the list. |
michael@0 | 12411 | nsTimeout* nextTimeout = timeout->getNext(); |
michael@0 | 12412 | |
michael@0 | 12413 | // It is safe to remove and re-insert because mWhen is now |
michael@0 | 12414 | // strictly smaller than it used to be, so we know we'll insert |
michael@0 | 12415 | // |timeout| before nextTimeout. |
michael@0 | 12416 | NS_ASSERTION(!nextTimeout || |
michael@0 | 12417 | timeout->mWhen < nextTimeout->mWhen, "How did that happen?"); |
michael@0 | 12418 | timeout->remove(); |
michael@0 | 12419 | // InsertTimeoutIntoList will addref |timeout| and reset |
michael@0 | 12420 | // mFiringDepth. Make sure to undo that after calling it. |
michael@0 | 12421 | uint32_t firingDepth = timeout->mFiringDepth; |
michael@0 | 12422 | InsertTimeoutIntoList(timeout); |
michael@0 | 12423 | timeout->mFiringDepth = firingDepth; |
michael@0 | 12424 | timeout->Release(); |
michael@0 | 12425 | |
michael@0 | 12426 | nsresult rv = timeout->InitTimer(TimerCallback, delay.ToMilliseconds()); |
michael@0 | 12427 | |
michael@0 | 12428 | if (NS_FAILED(rv)) { |
michael@0 | 12429 | NS_WARNING("Error resetting non background timer for DOM timeout!"); |
michael@0 | 12430 | return rv; |
michael@0 | 12431 | } |
michael@0 | 12432 | |
michael@0 | 12433 | timeout = nextTimeout; |
michael@0 | 12434 | } else { |
michael@0 | 12435 | timeout = timeout->getNext(); |
michael@0 | 12436 | } |
michael@0 | 12437 | } |
michael@0 | 12438 | |
michael@0 | 12439 | return NS_OK; |
michael@0 | 12440 | } |
michael@0 | 12441 | |
michael@0 | 12442 | void |
michael@0 | 12443 | nsGlobalWindow::ClearAllTimeouts() |
michael@0 | 12444 | { |
michael@0 | 12445 | nsTimeout *timeout, *nextTimeout; |
michael@0 | 12446 | |
michael@0 | 12447 | for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) { |
michael@0 | 12448 | /* If RunTimeout() is higher up on the stack for this |
michael@0 | 12449 | window, e.g. as a result of document.write from a timeout, |
michael@0 | 12450 | then we need to reset the list insertion point for |
michael@0 | 12451 | newly-created timeouts in case the user adds a timeout, |
michael@0 | 12452 | before we pop the stack back to RunTimeout. */ |
michael@0 | 12453 | if (mRunningTimeout == timeout) |
michael@0 | 12454 | mTimeoutInsertionPoint = nullptr; |
michael@0 | 12455 | |
michael@0 | 12456 | nextTimeout = timeout->getNext(); |
michael@0 | 12457 | |
michael@0 | 12458 | if (timeout->mTimer) { |
michael@0 | 12459 | timeout->mTimer->Cancel(); |
michael@0 | 12460 | timeout->mTimer = nullptr; |
michael@0 | 12461 | |
michael@0 | 12462 | // Drop the count since the timer isn't going to hold on |
michael@0 | 12463 | // anymore. |
michael@0 | 12464 | timeout->Release(); |
michael@0 | 12465 | } |
michael@0 | 12466 | |
michael@0 | 12467 | // Set timeout->mCleared to true to indicate that the timeout was |
michael@0 | 12468 | // cleared and taken out of the list of timeouts |
michael@0 | 12469 | timeout->mCleared = true; |
michael@0 | 12470 | |
michael@0 | 12471 | // Drop the count since we're removing it from the list. |
michael@0 | 12472 | timeout->Release(); |
michael@0 | 12473 | } |
michael@0 | 12474 | |
michael@0 | 12475 | // Clear out our list |
michael@0 | 12476 | mTimeouts.clear(); |
michael@0 | 12477 | } |
michael@0 | 12478 | |
michael@0 | 12479 | void |
michael@0 | 12480 | nsGlobalWindow::InsertTimeoutIntoList(nsTimeout *aTimeout) |
michael@0 | 12481 | { |
michael@0 | 12482 | NS_ASSERTION(IsInnerWindow(), |
michael@0 | 12483 | "InsertTimeoutIntoList() called on outer window!"); |
michael@0 | 12484 | |
michael@0 | 12485 | // Start at mLastTimeout and go backwards. Don't go further than |
michael@0 | 12486 | // mTimeoutInsertionPoint, though. This optimizes for the common case of |
michael@0 | 12487 | // insertion at the end. |
michael@0 | 12488 | nsTimeout* prevSibling; |
michael@0 | 12489 | for (prevSibling = mTimeouts.getLast(); |
michael@0 | 12490 | prevSibling && prevSibling != mTimeoutInsertionPoint && |
michael@0 | 12491 | // This condition needs to match the one in SetTimeoutOrInterval that |
michael@0 | 12492 | // determines whether to set mWhen or mTimeRemaining. |
michael@0 | 12493 | ((IsFrozen() || mTimeoutsSuspendDepth) ? |
michael@0 | 12494 | prevSibling->mTimeRemaining > aTimeout->mTimeRemaining : |
michael@0 | 12495 | prevSibling->mWhen > aTimeout->mWhen); |
michael@0 | 12496 | prevSibling = prevSibling->getPrevious()) { |
michael@0 | 12497 | /* Do nothing; just searching */ |
michael@0 | 12498 | } |
michael@0 | 12499 | |
michael@0 | 12500 | // Now link in aTimeout after prevSibling. |
michael@0 | 12501 | if (prevSibling) { |
michael@0 | 12502 | prevSibling->setNext(aTimeout); |
michael@0 | 12503 | } else { |
michael@0 | 12504 | mTimeouts.insertFront(aTimeout); |
michael@0 | 12505 | } |
michael@0 | 12506 | |
michael@0 | 12507 | aTimeout->mFiringDepth = 0; |
michael@0 | 12508 | |
michael@0 | 12509 | // Increment the timeout's reference count since it's now held on to |
michael@0 | 12510 | // by the list |
michael@0 | 12511 | aTimeout->AddRef(); |
michael@0 | 12512 | } |
michael@0 | 12513 | |
michael@0 | 12514 | // static |
michael@0 | 12515 | void |
michael@0 | 12516 | nsGlobalWindow::TimerCallback(nsITimer *aTimer, void *aClosure) |
michael@0 | 12517 | { |
michael@0 | 12518 | nsRefPtr<nsTimeout> timeout = (nsTimeout *)aClosure; |
michael@0 | 12519 | |
michael@0 | 12520 | timeout->mWindow->RunTimeout(timeout); |
michael@0 | 12521 | } |
michael@0 | 12522 | |
michael@0 | 12523 | //***************************************************************************** |
michael@0 | 12524 | // nsGlobalWindow: Helper Functions |
michael@0 | 12525 | //***************************************************************************** |
michael@0 | 12526 | |
michael@0 | 12527 | already_AddRefed<nsIDocShellTreeOwner> |
michael@0 | 12528 | nsGlobalWindow::GetTreeOwner() |
michael@0 | 12529 | { |
michael@0 | 12530 | FORWARD_TO_OUTER(GetTreeOwner, (), nullptr); |
michael@0 | 12531 | |
michael@0 | 12532 | // If there's no docShellAsItem, this window must have been closed, |
michael@0 | 12533 | // in that case there is no tree owner. |
michael@0 | 12534 | |
michael@0 | 12535 | if (!mDocShell) { |
michael@0 | 12536 | return nullptr; |
michael@0 | 12537 | } |
michael@0 | 12538 | |
michael@0 | 12539 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner; |
michael@0 | 12540 | mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); |
michael@0 | 12541 | return treeOwner.forget(); |
michael@0 | 12542 | } |
michael@0 | 12543 | |
michael@0 | 12544 | already_AddRefed<nsIBaseWindow> |
michael@0 | 12545 | nsGlobalWindow::GetTreeOwnerWindow() |
michael@0 | 12546 | { |
michael@0 | 12547 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 12548 | |
michael@0 | 12549 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner; |
michael@0 | 12550 | |
michael@0 | 12551 | // If there's no mDocShell, this window must have been closed, |
michael@0 | 12552 | // in that case there is no tree owner. |
michael@0 | 12553 | |
michael@0 | 12554 | if (mDocShell) { |
michael@0 | 12555 | mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); |
michael@0 | 12556 | } |
michael@0 | 12557 | |
michael@0 | 12558 | nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner); |
michael@0 | 12559 | return baseWindow.forget(); |
michael@0 | 12560 | } |
michael@0 | 12561 | |
michael@0 | 12562 | already_AddRefed<nsIWebBrowserChrome> |
michael@0 | 12563 | nsGlobalWindow::GetWebBrowserChrome() |
michael@0 | 12564 | { |
michael@0 | 12565 | nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner(); |
michael@0 | 12566 | |
michael@0 | 12567 | nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(treeOwner); |
michael@0 | 12568 | return browserChrome.forget(); |
michael@0 | 12569 | } |
michael@0 | 12570 | |
michael@0 | 12571 | nsIScrollableFrame * |
michael@0 | 12572 | nsGlobalWindow::GetScrollFrame() |
michael@0 | 12573 | { |
michael@0 | 12574 | FORWARD_TO_OUTER(GetScrollFrame, (), nullptr); |
michael@0 | 12575 | |
michael@0 | 12576 | if (!mDocShell) { |
michael@0 | 12577 | return nullptr; |
michael@0 | 12578 | } |
michael@0 | 12579 | |
michael@0 | 12580 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 12581 | if (presShell) { |
michael@0 | 12582 | return presShell->GetRootScrollFrameAsScrollable(); |
michael@0 | 12583 | } |
michael@0 | 12584 | return nullptr; |
michael@0 | 12585 | } |
michael@0 | 12586 | |
michael@0 | 12587 | nsresult |
michael@0 | 12588 | nsGlobalWindow::SecurityCheckURL(const char *aURL) |
michael@0 | 12589 | { |
michael@0 | 12590 | nsCOMPtr<nsPIDOMWindow> sourceWindow; |
michael@0 | 12591 | JSContext* topCx = nsContentUtils::GetCurrentJSContext(); |
michael@0 | 12592 | if (topCx) { |
michael@0 | 12593 | sourceWindow = do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(topCx)); |
michael@0 | 12594 | } |
michael@0 | 12595 | if (!sourceWindow) { |
michael@0 | 12596 | sourceWindow = this; |
michael@0 | 12597 | } |
michael@0 | 12598 | AutoJSContext cx; |
michael@0 | 12599 | nsGlobalWindow* sourceWin = static_cast<nsGlobalWindow*>(sourceWindow.get()); |
michael@0 | 12600 | JSAutoCompartment ac(cx, sourceWin->GetGlobalJSObject()); |
michael@0 | 12601 | |
michael@0 | 12602 | // Resolve the baseURI, which could be relative to the calling window. |
michael@0 | 12603 | // |
michael@0 | 12604 | // Note the algorithm to get the base URI should match the one |
michael@0 | 12605 | // used to actually kick off the load in nsWindowWatcher.cpp. |
michael@0 | 12606 | nsCOMPtr<nsIDocument> doc = sourceWindow->GetDoc(); |
michael@0 | 12607 | nsIURI* baseURI = nullptr; |
michael@0 | 12608 | nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8 |
michael@0 | 12609 | if (doc) { |
michael@0 | 12610 | baseURI = doc->GetDocBaseURI(); |
michael@0 | 12611 | charset = doc->GetDocumentCharacterSet(); |
michael@0 | 12612 | } |
michael@0 | 12613 | nsCOMPtr<nsIURI> uri; |
michael@0 | 12614 | nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL), |
michael@0 | 12615 | charset.get(), baseURI); |
michael@0 | 12616 | if (NS_WARN_IF(NS_FAILED(rv))) { |
michael@0 | 12617 | return rv; |
michael@0 | 12618 | } |
michael@0 | 12619 | |
michael@0 | 12620 | if (NS_FAILED(nsContentUtils::GetSecurityManager()-> |
michael@0 | 12621 | CheckLoadURIFromScript(cx, uri))) { |
michael@0 | 12622 | return NS_ERROR_FAILURE; |
michael@0 | 12623 | } |
michael@0 | 12624 | |
michael@0 | 12625 | return NS_OK; |
michael@0 | 12626 | } |
michael@0 | 12627 | |
michael@0 | 12628 | void |
michael@0 | 12629 | nsGlobalWindow::FlushPendingNotifications(mozFlushType aType) |
michael@0 | 12630 | { |
michael@0 | 12631 | if (mDoc) { |
michael@0 | 12632 | mDoc->FlushPendingNotifications(aType); |
michael@0 | 12633 | } |
michael@0 | 12634 | } |
michael@0 | 12635 | |
michael@0 | 12636 | void |
michael@0 | 12637 | nsGlobalWindow::EnsureSizeUpToDate() |
michael@0 | 12638 | { |
michael@0 | 12639 | MOZ_ASSERT(IsOuterWindow()); |
michael@0 | 12640 | |
michael@0 | 12641 | // If we're a subframe, make sure our size is up to date. It's OK that this |
michael@0 | 12642 | // crosses the content/chrome boundary, since chrome can have pending reflows |
michael@0 | 12643 | // too. |
michael@0 | 12644 | nsGlobalWindow *parent = |
michael@0 | 12645 | static_cast<nsGlobalWindow *>(GetPrivateParent()); |
michael@0 | 12646 | if (parent) { |
michael@0 | 12647 | parent->FlushPendingNotifications(Flush_Layout); |
michael@0 | 12648 | } |
michael@0 | 12649 | } |
michael@0 | 12650 | |
michael@0 | 12651 | already_AddRefed<nsISupports> |
michael@0 | 12652 | nsGlobalWindow::SaveWindowState() |
michael@0 | 12653 | { |
michael@0 | 12654 | NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state"); |
michael@0 | 12655 | |
michael@0 | 12656 | if (!mContext || !GetWrapperPreserveColor()) { |
michael@0 | 12657 | // The window may be getting torn down; don't bother saving state. |
michael@0 | 12658 | return nullptr; |
michael@0 | 12659 | } |
michael@0 | 12660 | |
michael@0 | 12661 | nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); |
michael@0 | 12662 | NS_ASSERTION(inner, "No inner window to save"); |
michael@0 | 12663 | |
michael@0 | 12664 | // Don't do anything else to this inner window! After this point, all |
michael@0 | 12665 | // calls to SetTimeoutOrInterval will create entries in the timeout |
michael@0 | 12666 | // list that will only run after this window has come out of the bfcache. |
michael@0 | 12667 | // Also, while we're frozen, we won't dispatch online/offline events |
michael@0 | 12668 | // to the page. |
michael@0 | 12669 | inner->Freeze(); |
michael@0 | 12670 | |
michael@0 | 12671 | nsCOMPtr<nsISupports> state = new WindowStateHolder(inner); |
michael@0 | 12672 | |
michael@0 | 12673 | #ifdef DEBUG_PAGE_CACHE |
michael@0 | 12674 | printf("saving window state, state = %p\n", (void*)state); |
michael@0 | 12675 | #endif |
michael@0 | 12676 | |
michael@0 | 12677 | return state.forget(); |
michael@0 | 12678 | } |
michael@0 | 12679 | |
michael@0 | 12680 | nsresult |
michael@0 | 12681 | nsGlobalWindow::RestoreWindowState(nsISupports *aState) |
michael@0 | 12682 | { |
michael@0 | 12683 | NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window"); |
michael@0 | 12684 | |
michael@0 | 12685 | if (!mContext || !GetWrapperPreserveColor()) { |
michael@0 | 12686 | // The window may be getting torn down; don't bother restoring state. |
michael@0 | 12687 | return NS_OK; |
michael@0 | 12688 | } |
michael@0 | 12689 | |
michael@0 | 12690 | nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState); |
michael@0 | 12691 | NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE); |
michael@0 | 12692 | |
michael@0 | 12693 | #ifdef DEBUG_PAGE_CACHE |
michael@0 | 12694 | printf("restoring window state, state = %p\n", (void*)holder); |
michael@0 | 12695 | #endif |
michael@0 | 12696 | |
michael@0 | 12697 | // And we're ready to go! |
michael@0 | 12698 | nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); |
michael@0 | 12699 | |
michael@0 | 12700 | // if a link is focused, refocus with the FLAG_SHOWRING flag set. This makes |
michael@0 | 12701 | // it easy to tell which link was last clicked when going back a page. |
michael@0 | 12702 | nsIContent* focusedNode = inner->GetFocusedNode(); |
michael@0 | 12703 | if (IsLink(focusedNode)) { |
michael@0 | 12704 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 12705 | if (fm) { |
michael@0 | 12706 | nsCOMPtr<nsIDOMElement> focusedElement(do_QueryInterface(focusedNode)); |
michael@0 | 12707 | fm->SetFocus(focusedElement, nsIFocusManager::FLAG_NOSCROLL | |
michael@0 | 12708 | nsIFocusManager::FLAG_SHOWRING); |
michael@0 | 12709 | } |
michael@0 | 12710 | } |
michael@0 | 12711 | |
michael@0 | 12712 | inner->Thaw(); |
michael@0 | 12713 | |
michael@0 | 12714 | holder->DidRestoreWindow(); |
michael@0 | 12715 | |
michael@0 | 12716 | return NS_OK; |
michael@0 | 12717 | } |
michael@0 | 12718 | |
michael@0 | 12719 | void |
michael@0 | 12720 | nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease, |
michael@0 | 12721 | bool aFreezeChildren) |
michael@0 | 12722 | { |
michael@0 | 12723 | FORWARD_TO_INNER_VOID(SuspendTimeouts, (aIncrease, aFreezeChildren)); |
michael@0 | 12724 | |
michael@0 | 12725 | bool suspended = (mTimeoutsSuspendDepth != 0); |
michael@0 | 12726 | mTimeoutsSuspendDepth += aIncrease; |
michael@0 | 12727 | |
michael@0 | 12728 | if (!suspended) { |
michael@0 | 12729 | nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); |
michael@0 | 12730 | if (ac) { |
michael@0 | 12731 | for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) |
michael@0 | 12732 | ac->RemoveWindowListener(mEnabledSensors[i], this); |
michael@0 | 12733 | } |
michael@0 | 12734 | DisableGamepadUpdates(); |
michael@0 | 12735 | |
michael@0 | 12736 | // Suspend all of the workers for this window. |
michael@0 | 12737 | mozilla::dom::workers::SuspendWorkersForWindow(this); |
michael@0 | 12738 | |
michael@0 | 12739 | TimeStamp now = TimeStamp::Now(); |
michael@0 | 12740 | for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) { |
michael@0 | 12741 | // Set mTimeRemaining to be the time remaining for this timer. |
michael@0 | 12742 | if (t->mWhen > now) |
michael@0 | 12743 | t->mTimeRemaining = t->mWhen - now; |
michael@0 | 12744 | else |
michael@0 | 12745 | t->mTimeRemaining = TimeDuration(0); |
michael@0 | 12746 | |
michael@0 | 12747 | // Drop the XPCOM timer; we'll reschedule when restoring the state. |
michael@0 | 12748 | if (t->mTimer) { |
michael@0 | 12749 | t->mTimer->Cancel(); |
michael@0 | 12750 | t->mTimer = nullptr; |
michael@0 | 12751 | |
michael@0 | 12752 | // Drop the reference that the timer's closure had on this timeout, we'll |
michael@0 | 12753 | // add it back in ResumeTimeouts. Note that it shouldn't matter that we're |
michael@0 | 12754 | // passing null for the context, since this shouldn't actually release this |
michael@0 | 12755 | // timeout. |
michael@0 | 12756 | t->Release(); |
michael@0 | 12757 | } |
michael@0 | 12758 | } |
michael@0 | 12759 | |
michael@0 | 12760 | // Suspend all of the AudioContexts for this window |
michael@0 | 12761 | for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
michael@0 | 12762 | mAudioContexts[i]->Suspend(); |
michael@0 | 12763 | } |
michael@0 | 12764 | } |
michael@0 | 12765 | |
michael@0 | 12766 | // Suspend our children as well. |
michael@0 | 12767 | nsCOMPtr<nsIDocShell> docShell = GetDocShell(); |
michael@0 | 12768 | if (docShell) { |
michael@0 | 12769 | int32_t childCount = 0; |
michael@0 | 12770 | docShell->GetChildCount(&childCount); |
michael@0 | 12771 | |
michael@0 | 12772 | for (int32_t i = 0; i < childCount; ++i) { |
michael@0 | 12773 | nsCOMPtr<nsIDocShellTreeItem> childShell; |
michael@0 | 12774 | docShell->GetChildAt(i, getter_AddRefs(childShell)); |
michael@0 | 12775 | NS_ASSERTION(childShell, "null child shell"); |
michael@0 | 12776 | |
michael@0 | 12777 | nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell); |
michael@0 | 12778 | if (pWin) { |
michael@0 | 12779 | nsGlobalWindow *win = |
michael@0 | 12780 | static_cast<nsGlobalWindow*> |
michael@0 | 12781 | (static_cast<nsPIDOMWindow*>(pWin)); |
michael@0 | 12782 | NS_ASSERTION(win->IsOuterWindow(), "Expected outer window"); |
michael@0 | 12783 | nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal(); |
michael@0 | 12784 | |
michael@0 | 12785 | // This is a bit hackish. Only freeze/suspend windows which are truly our |
michael@0 | 12786 | // subwindows. |
michael@0 | 12787 | nsCOMPtr<Element> frame = pWin->GetFrameElementInternal(); |
michael@0 | 12788 | if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) { |
michael@0 | 12789 | continue; |
michael@0 | 12790 | } |
michael@0 | 12791 | |
michael@0 | 12792 | win->SuspendTimeouts(aIncrease, aFreezeChildren); |
michael@0 | 12793 | |
michael@0 | 12794 | if (inner && aFreezeChildren) { |
michael@0 | 12795 | inner->Freeze(); |
michael@0 | 12796 | } |
michael@0 | 12797 | } |
michael@0 | 12798 | } |
michael@0 | 12799 | } |
michael@0 | 12800 | } |
michael@0 | 12801 | |
michael@0 | 12802 | nsresult |
michael@0 | 12803 | nsGlobalWindow::ResumeTimeouts(bool aThawChildren) |
michael@0 | 12804 | { |
michael@0 | 12805 | FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED); |
michael@0 | 12806 | |
michael@0 | 12807 | NS_ASSERTION(mTimeoutsSuspendDepth, "Mismatched calls to ResumeTimeouts!"); |
michael@0 | 12808 | --mTimeoutsSuspendDepth; |
michael@0 | 12809 | bool shouldResume = (mTimeoutsSuspendDepth == 0) && !mInnerObjectsFreed; |
michael@0 | 12810 | nsresult rv; |
michael@0 | 12811 | |
michael@0 | 12812 | if (shouldResume) { |
michael@0 | 12813 | nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); |
michael@0 | 12814 | if (ac) { |
michael@0 | 12815 | for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) |
michael@0 | 12816 | ac->AddWindowListener(mEnabledSensors[i], this); |
michael@0 | 12817 | } |
michael@0 | 12818 | EnableGamepadUpdates(); |
michael@0 | 12819 | |
michael@0 | 12820 | // Resume all of the AudioContexts for this window |
michael@0 | 12821 | for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
michael@0 | 12822 | mAudioContexts[i]->Resume(); |
michael@0 | 12823 | } |
michael@0 | 12824 | |
michael@0 | 12825 | // Resume all of the workers for this window. |
michael@0 | 12826 | mozilla::dom::workers::ResumeWorkersForWindow(this); |
michael@0 | 12827 | |
michael@0 | 12828 | // Restore all of the timeouts, using the stored time remaining |
michael@0 | 12829 | // (stored in timeout->mTimeRemaining). |
michael@0 | 12830 | |
michael@0 | 12831 | TimeStamp now = TimeStamp::Now(); |
michael@0 | 12832 | |
michael@0 | 12833 | #ifdef DEBUG |
michael@0 | 12834 | bool _seenDummyTimeout = false; |
michael@0 | 12835 | #endif |
michael@0 | 12836 | |
michael@0 | 12837 | for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) { |
michael@0 | 12838 | // There's a chance we're being called with RunTimeout on the stack in which |
michael@0 | 12839 | // case we have a dummy timeout in the list that *must not* be resumed. It |
michael@0 | 12840 | // can be identified by a null mWindow. |
michael@0 | 12841 | if (!t->mWindow) { |
michael@0 | 12842 | #ifdef DEBUG |
michael@0 | 12843 | NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!"); |
michael@0 | 12844 | _seenDummyTimeout = true; |
michael@0 | 12845 | #endif |
michael@0 | 12846 | continue; |
michael@0 | 12847 | } |
michael@0 | 12848 | |
michael@0 | 12849 | // XXXbz the combination of the way |delay| and |t->mWhen| are set here |
michael@0 | 12850 | // makes no sense. Are we trying to impose that min timeout value or |
michael@0 | 12851 | // not??? |
michael@0 | 12852 | uint32_t delay = |
michael@0 | 12853 | std::max(int32_t(t->mTimeRemaining.ToMilliseconds()), |
michael@0 | 12854 | DOMMinTimeoutValue()); |
michael@0 | 12855 | |
michael@0 | 12856 | // Set mWhen back to the time when the timer is supposed to |
michael@0 | 12857 | // fire. |
michael@0 | 12858 | t->mWhen = now + t->mTimeRemaining; |
michael@0 | 12859 | |
michael@0 | 12860 | t->mTimer = do_CreateInstance("@mozilla.org/timer;1"); |
michael@0 | 12861 | NS_ENSURE_TRUE(t->mTimer, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 12862 | |
michael@0 | 12863 | rv = t->InitTimer(TimerCallback, delay); |
michael@0 | 12864 | if (NS_FAILED(rv)) { |
michael@0 | 12865 | t->mTimer = nullptr; |
michael@0 | 12866 | return rv; |
michael@0 | 12867 | } |
michael@0 | 12868 | |
michael@0 | 12869 | // Add a reference for the new timer's closure. |
michael@0 | 12870 | t->AddRef(); |
michael@0 | 12871 | } |
michael@0 | 12872 | } |
michael@0 | 12873 | |
michael@0 | 12874 | // Resume our children as well. |
michael@0 | 12875 | nsCOMPtr<nsIDocShell> docShell = GetDocShell(); |
michael@0 | 12876 | if (docShell) { |
michael@0 | 12877 | int32_t childCount = 0; |
michael@0 | 12878 | docShell->GetChildCount(&childCount); |
michael@0 | 12879 | |
michael@0 | 12880 | for (int32_t i = 0; i < childCount; ++i) { |
michael@0 | 12881 | nsCOMPtr<nsIDocShellTreeItem> childShell; |
michael@0 | 12882 | docShell->GetChildAt(i, getter_AddRefs(childShell)); |
michael@0 | 12883 | NS_ASSERTION(childShell, "null child shell"); |
michael@0 | 12884 | |
michael@0 | 12885 | nsCOMPtr<nsPIDOMWindow> pWin = do_GetInterface(childShell); |
michael@0 | 12886 | if (pWin) { |
michael@0 | 12887 | nsGlobalWindow *win = |
michael@0 | 12888 | static_cast<nsGlobalWindow*> |
michael@0 | 12889 | (static_cast<nsPIDOMWindow*>(pWin)); |
michael@0 | 12890 | |
michael@0 | 12891 | NS_ASSERTION(win->IsOuterWindow(), "Expected outer window"); |
michael@0 | 12892 | nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal(); |
michael@0 | 12893 | |
michael@0 | 12894 | // This is a bit hackish. Only thaw/resume windows which are truly our |
michael@0 | 12895 | // subwindows. |
michael@0 | 12896 | nsCOMPtr<Element> frame = pWin->GetFrameElementInternal(); |
michael@0 | 12897 | if (!mDoc || !frame || mDoc != frame->OwnerDoc() || !inner) { |
michael@0 | 12898 | continue; |
michael@0 | 12899 | } |
michael@0 | 12900 | |
michael@0 | 12901 | if (inner && aThawChildren) { |
michael@0 | 12902 | inner->Thaw(); |
michael@0 | 12903 | } |
michael@0 | 12904 | |
michael@0 | 12905 | rv = win->ResumeTimeouts(aThawChildren); |
michael@0 | 12906 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 12907 | } |
michael@0 | 12908 | } |
michael@0 | 12909 | } |
michael@0 | 12910 | |
michael@0 | 12911 | return NS_OK; |
michael@0 | 12912 | } |
michael@0 | 12913 | |
michael@0 | 12914 | uint32_t |
michael@0 | 12915 | nsGlobalWindow::TimeoutSuspendCount() |
michael@0 | 12916 | { |
michael@0 | 12917 | FORWARD_TO_INNER(TimeoutSuspendCount, (), 0); |
michael@0 | 12918 | return mTimeoutsSuspendDepth; |
michael@0 | 12919 | } |
michael@0 | 12920 | |
michael@0 | 12921 | void |
michael@0 | 12922 | nsGlobalWindow::EnableDeviceSensor(uint32_t aType) |
michael@0 | 12923 | { |
michael@0 | 12924 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 12925 | |
michael@0 | 12926 | bool alreadyEnabled = false; |
michael@0 | 12927 | for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) { |
michael@0 | 12928 | if (mEnabledSensors[i] == aType) { |
michael@0 | 12929 | alreadyEnabled = true; |
michael@0 | 12930 | break; |
michael@0 | 12931 | } |
michael@0 | 12932 | } |
michael@0 | 12933 | |
michael@0 | 12934 | mEnabledSensors.AppendElement(aType); |
michael@0 | 12935 | |
michael@0 | 12936 | if (alreadyEnabled) { |
michael@0 | 12937 | return; |
michael@0 | 12938 | } |
michael@0 | 12939 | |
michael@0 | 12940 | nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); |
michael@0 | 12941 | if (ac) { |
michael@0 | 12942 | ac->AddWindowListener(aType, this); |
michael@0 | 12943 | } |
michael@0 | 12944 | } |
michael@0 | 12945 | |
michael@0 | 12946 | void |
michael@0 | 12947 | nsGlobalWindow::DisableDeviceSensor(uint32_t aType) |
michael@0 | 12948 | { |
michael@0 | 12949 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 12950 | |
michael@0 | 12951 | int32_t doomedElement = -1; |
michael@0 | 12952 | int32_t listenerCount = 0; |
michael@0 | 12953 | for (uint32_t i = 0; i < mEnabledSensors.Length(); i++) { |
michael@0 | 12954 | if (mEnabledSensors[i] == aType) { |
michael@0 | 12955 | doomedElement = i; |
michael@0 | 12956 | listenerCount++; |
michael@0 | 12957 | } |
michael@0 | 12958 | } |
michael@0 | 12959 | |
michael@0 | 12960 | if (doomedElement == -1) { |
michael@0 | 12961 | return; |
michael@0 | 12962 | } |
michael@0 | 12963 | |
michael@0 | 12964 | mEnabledSensors.RemoveElementAt(doomedElement); |
michael@0 | 12965 | |
michael@0 | 12966 | if (listenerCount > 1) { |
michael@0 | 12967 | return; |
michael@0 | 12968 | } |
michael@0 | 12969 | |
michael@0 | 12970 | nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); |
michael@0 | 12971 | if (ac) { |
michael@0 | 12972 | ac->RemoveWindowListener(aType, this); |
michael@0 | 12973 | } |
michael@0 | 12974 | } |
michael@0 | 12975 | |
michael@0 | 12976 | void |
michael@0 | 12977 | nsGlobalWindow::SetHasGamepadEventListener(bool aHasGamepad/* = true*/) |
michael@0 | 12978 | { |
michael@0 | 12979 | FORWARD_TO_INNER_VOID(SetHasGamepadEventListener, (aHasGamepad)); |
michael@0 | 12980 | mHasGamepad = aHasGamepad; |
michael@0 | 12981 | if (aHasGamepad) { |
michael@0 | 12982 | EnableGamepadUpdates(); |
michael@0 | 12983 | } |
michael@0 | 12984 | } |
michael@0 | 12985 | |
michael@0 | 12986 | void |
michael@0 | 12987 | nsGlobalWindow::EnableTimeChangeNotifications() |
michael@0 | 12988 | { |
michael@0 | 12989 | mozilla::time::AddWindowListener(this); |
michael@0 | 12990 | } |
michael@0 | 12991 | |
michael@0 | 12992 | void |
michael@0 | 12993 | nsGlobalWindow::DisableTimeChangeNotifications() |
michael@0 | 12994 | { |
michael@0 | 12995 | mozilla::time::RemoveWindowListener(this); |
michael@0 | 12996 | } |
michael@0 | 12997 | |
michael@0 | 12998 | static PLDHashOperator |
michael@0 | 12999 | CollectSizeAndListenerCount( |
michael@0 | 13000 | nsPtrHashKey<DOMEventTargetHelper>* aEntry, |
michael@0 | 13001 | void *arg) |
michael@0 | 13002 | { |
michael@0 | 13003 | nsWindowSizes* windowSizes = static_cast<nsWindowSizes*>(arg); |
michael@0 | 13004 | |
michael@0 | 13005 | DOMEventTargetHelper* et = aEntry->GetKey(); |
michael@0 | 13006 | |
michael@0 | 13007 | if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) { |
michael@0 | 13008 | windowSizes->mDOMEventTargetsSize += |
michael@0 | 13009 | iSizeOf->SizeOfEventTargetIncludingThis(windowSizes->mMallocSizeOf); |
michael@0 | 13010 | } |
michael@0 | 13011 | |
michael@0 | 13012 | if (EventListenerManager* elm = et->GetExistingListenerManager()) { |
michael@0 | 13013 | windowSizes->mDOMEventListenersCount += elm->ListenerCount(); |
michael@0 | 13014 | } |
michael@0 | 13015 | |
michael@0 | 13016 | return PL_DHASH_NEXT; |
michael@0 | 13017 | } |
michael@0 | 13018 | |
michael@0 | 13019 | void |
michael@0 | 13020 | nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const |
michael@0 | 13021 | { |
michael@0 | 13022 | aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this); |
michael@0 | 13023 | |
michael@0 | 13024 | if (IsInnerWindow()) { |
michael@0 | 13025 | EventListenerManager* elm = GetExistingListenerManager(); |
michael@0 | 13026 | if (elm) { |
michael@0 | 13027 | aWindowSizes->mDOMOtherSize += |
michael@0 | 13028 | elm->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 13029 | aWindowSizes->mDOMEventListenersCount += |
michael@0 | 13030 | elm->ListenerCount(); |
michael@0 | 13031 | } |
michael@0 | 13032 | if (mDoc) { |
michael@0 | 13033 | mDoc->DocAddSizeOfIncludingThis(aWindowSizes); |
michael@0 | 13034 | } |
michael@0 | 13035 | } |
michael@0 | 13036 | |
michael@0 | 13037 | if (mNavigator) { |
michael@0 | 13038 | aWindowSizes->mDOMOtherSize += |
michael@0 | 13039 | mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf); |
michael@0 | 13040 | } |
michael@0 | 13041 | |
michael@0 | 13042 | // The things pointed to by the entries will be measured below, so we |
michael@0 | 13043 | // use nullptr for the callback here. |
michael@0 | 13044 | aWindowSizes->mDOMEventTargetsSize += |
michael@0 | 13045 | mEventTargetObjects.SizeOfExcludingThis(nullptr, |
michael@0 | 13046 | aWindowSizes->mMallocSizeOf); |
michael@0 | 13047 | aWindowSizes->mDOMEventTargetsCount += |
michael@0 | 13048 | const_cast<nsTHashtable<nsPtrHashKey<DOMEventTargetHelper> >*> |
michael@0 | 13049 | (&mEventTargetObjects)->EnumerateEntries(CollectSizeAndListenerCount, |
michael@0 | 13050 | aWindowSizes); |
michael@0 | 13051 | } |
michael@0 | 13052 | |
michael@0 | 13053 | |
michael@0 | 13054 | #ifdef MOZ_GAMEPAD |
michael@0 | 13055 | void |
michael@0 | 13056 | nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad) |
michael@0 | 13057 | { |
michael@0 | 13058 | FORWARD_TO_INNER_VOID(AddGamepad, (aIndex, aGamepad)); |
michael@0 | 13059 | mGamepads.Put(aIndex, aGamepad); |
michael@0 | 13060 | } |
michael@0 | 13061 | |
michael@0 | 13062 | void |
michael@0 | 13063 | nsGlobalWindow::RemoveGamepad(uint32_t aIndex) |
michael@0 | 13064 | { |
michael@0 | 13065 | FORWARD_TO_INNER_VOID(RemoveGamepad, (aIndex)); |
michael@0 | 13066 | mGamepads.Remove(aIndex); |
michael@0 | 13067 | } |
michael@0 | 13068 | |
michael@0 | 13069 | // static |
michael@0 | 13070 | PLDHashOperator |
michael@0 | 13071 | nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData, |
michael@0 | 13072 | void* aUserArg) |
michael@0 | 13073 | { |
michael@0 | 13074 | nsTArray<nsRefPtr<Gamepad> >* array = |
michael@0 | 13075 | static_cast<nsTArray<nsRefPtr<Gamepad> >*>(aUserArg); |
michael@0 | 13076 | array->EnsureLengthAtLeast(aKey + 1); |
michael@0 | 13077 | (*array)[aKey] = aData; |
michael@0 | 13078 | return PL_DHASH_NEXT; |
michael@0 | 13079 | } |
michael@0 | 13080 | |
michael@0 | 13081 | void |
michael@0 | 13082 | nsGlobalWindow::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads) |
michael@0 | 13083 | { |
michael@0 | 13084 | FORWARD_TO_INNER_VOID(GetGamepads, (aGamepads)); |
michael@0 | 13085 | aGamepads.Clear(); |
michael@0 | 13086 | // mGamepads.Count() may not be sufficient, but it's not harmful. |
michael@0 | 13087 | aGamepads.SetCapacity(mGamepads.Count()); |
michael@0 | 13088 | mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads); |
michael@0 | 13089 | } |
michael@0 | 13090 | |
michael@0 | 13091 | already_AddRefed<Gamepad> |
michael@0 | 13092 | nsGlobalWindow::GetGamepad(uint32_t aIndex) |
michael@0 | 13093 | { |
michael@0 | 13094 | FORWARD_TO_INNER(GetGamepad, (aIndex), nullptr); |
michael@0 | 13095 | nsRefPtr<Gamepad> gamepad; |
michael@0 | 13096 | if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) { |
michael@0 | 13097 | return gamepad.forget(); |
michael@0 | 13098 | } |
michael@0 | 13099 | |
michael@0 | 13100 | return nullptr; |
michael@0 | 13101 | } |
michael@0 | 13102 | |
michael@0 | 13103 | void |
michael@0 | 13104 | nsGlobalWindow::SetHasSeenGamepadInput(bool aHasSeen) |
michael@0 | 13105 | { |
michael@0 | 13106 | FORWARD_TO_INNER_VOID(SetHasSeenGamepadInput, (aHasSeen)); |
michael@0 | 13107 | mHasSeenGamepadInput = aHasSeen; |
michael@0 | 13108 | } |
michael@0 | 13109 | |
michael@0 | 13110 | bool |
michael@0 | 13111 | nsGlobalWindow::HasSeenGamepadInput() |
michael@0 | 13112 | { |
michael@0 | 13113 | FORWARD_TO_INNER(HasSeenGamepadInput, (), false); |
michael@0 | 13114 | return mHasSeenGamepadInput; |
michael@0 | 13115 | } |
michael@0 | 13116 | |
michael@0 | 13117 | // static |
michael@0 | 13118 | PLDHashOperator |
michael@0 | 13119 | nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, Gamepad* aData, |
michael@0 | 13120 | void* aUserArg) |
michael@0 | 13121 | { |
michael@0 | 13122 | nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService()); |
michael@0 | 13123 | gamepadsvc->SyncGamepadState(aKey, aData); |
michael@0 | 13124 | return PL_DHASH_NEXT; |
michael@0 | 13125 | } |
michael@0 | 13126 | |
michael@0 | 13127 | void |
michael@0 | 13128 | nsGlobalWindow::SyncGamepadState() |
michael@0 | 13129 | { |
michael@0 | 13130 | FORWARD_TO_INNER_VOID(SyncGamepadState, ()); |
michael@0 | 13131 | if (mHasSeenGamepadInput) { |
michael@0 | 13132 | mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr); |
michael@0 | 13133 | } |
michael@0 | 13134 | } |
michael@0 | 13135 | #endif |
michael@0 | 13136 | // nsGlobalChromeWindow implementation |
michael@0 | 13137 | |
michael@0 | 13138 | NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow) |
michael@0 | 13139 | |
michael@0 | 13140 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow, |
michael@0 | 13141 | nsGlobalWindow) |
michael@0 | 13142 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow) |
michael@0 | 13143 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) |
michael@0 | 13144 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 13145 | |
michael@0 | 13146 | |
michael@0 | 13147 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow, |
michael@0 | 13148 | nsGlobalWindow) |
michael@0 | 13149 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserDOMWindow) |
michael@0 | 13150 | if (tmp->mMessageManager) { |
michael@0 | 13151 | static_cast<nsFrameMessageManager*>( |
michael@0 | 13152 | tmp->mMessageManager.get())->Disconnect(); |
michael@0 | 13153 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager) |
michael@0 | 13154 | } |
michael@0 | 13155 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 13156 | |
michael@0 | 13157 | DOMCI_DATA(ChromeWindow, nsGlobalChromeWindow) |
michael@0 | 13158 | |
michael@0 | 13159 | // QueryInterface implementation for nsGlobalChromeWindow |
michael@0 | 13160 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalChromeWindow) |
michael@0 | 13161 | NS_INTERFACE_MAP_ENTRY(nsIDOMChromeWindow) |
michael@0 | 13162 | NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ChromeWindow) |
michael@0 | 13163 | NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow) |
michael@0 | 13164 | |
michael@0 | 13165 | NS_IMPL_ADDREF_INHERITED(nsGlobalChromeWindow, nsGlobalWindow) |
michael@0 | 13166 | NS_IMPL_RELEASE_INHERITED(nsGlobalChromeWindow, nsGlobalWindow) |
michael@0 | 13167 | |
michael@0 | 13168 | NS_IMETHODIMP |
michael@0 | 13169 | nsGlobalChromeWindow::GetWindowState(uint16_t* aWindowState) |
michael@0 | 13170 | { |
michael@0 | 13171 | *aWindowState = WindowState(); |
michael@0 | 13172 | return NS_OK; |
michael@0 | 13173 | } |
michael@0 | 13174 | |
michael@0 | 13175 | uint16_t |
michael@0 | 13176 | nsGlobalWindow::WindowState() |
michael@0 | 13177 | { |
michael@0 | 13178 | nsCOMPtr<nsIWidget> widget = GetMainWidget(); |
michael@0 | 13179 | |
michael@0 | 13180 | int32_t mode = widget ? widget->SizeMode() : 0; |
michael@0 | 13181 | |
michael@0 | 13182 | switch (mode) { |
michael@0 | 13183 | case nsSizeMode_Minimized: |
michael@0 | 13184 | return nsIDOMChromeWindow::STATE_MINIMIZED; |
michael@0 | 13185 | case nsSizeMode_Maximized: |
michael@0 | 13186 | return nsIDOMChromeWindow::STATE_MAXIMIZED; |
michael@0 | 13187 | case nsSizeMode_Fullscreen: |
michael@0 | 13188 | return nsIDOMChromeWindow::STATE_FULLSCREEN; |
michael@0 | 13189 | case nsSizeMode_Normal: |
michael@0 | 13190 | return nsIDOMChromeWindow::STATE_NORMAL; |
michael@0 | 13191 | default: |
michael@0 | 13192 | NS_WARNING("Illegal window state for this chrome window"); |
michael@0 | 13193 | break; |
michael@0 | 13194 | } |
michael@0 | 13195 | |
michael@0 | 13196 | return nsIDOMChromeWindow::STATE_NORMAL; |
michael@0 | 13197 | } |
michael@0 | 13198 | |
michael@0 | 13199 | NS_IMETHODIMP |
michael@0 | 13200 | nsGlobalChromeWindow::Maximize() |
michael@0 | 13201 | { |
michael@0 | 13202 | ErrorResult rv; |
michael@0 | 13203 | Maximize(rv); |
michael@0 | 13204 | return rv.ErrorCode(); |
michael@0 | 13205 | } |
michael@0 | 13206 | |
michael@0 | 13207 | void |
michael@0 | 13208 | nsGlobalWindow::Maximize(ErrorResult& aError) |
michael@0 | 13209 | { |
michael@0 | 13210 | nsCOMPtr<nsIWidget> widget = GetMainWidget(); |
michael@0 | 13211 | |
michael@0 | 13212 | if (widget) { |
michael@0 | 13213 | aError = widget->SetSizeMode(nsSizeMode_Maximized); |
michael@0 | 13214 | } |
michael@0 | 13215 | } |
michael@0 | 13216 | |
michael@0 | 13217 | NS_IMETHODIMP |
michael@0 | 13218 | nsGlobalChromeWindow::Minimize() |
michael@0 | 13219 | { |
michael@0 | 13220 | ErrorResult rv; |
michael@0 | 13221 | Minimize(rv); |
michael@0 | 13222 | return rv.ErrorCode(); |
michael@0 | 13223 | } |
michael@0 | 13224 | |
michael@0 | 13225 | void |
michael@0 | 13226 | nsGlobalWindow::Minimize(ErrorResult& aError) |
michael@0 | 13227 | { |
michael@0 | 13228 | nsCOMPtr<nsIWidget> widget = GetMainWidget(); |
michael@0 | 13229 | |
michael@0 | 13230 | if (widget) { |
michael@0 | 13231 | aError = widget->SetSizeMode(nsSizeMode_Minimized); |
michael@0 | 13232 | } |
michael@0 | 13233 | } |
michael@0 | 13234 | |
michael@0 | 13235 | NS_IMETHODIMP |
michael@0 | 13236 | nsGlobalChromeWindow::Restore() |
michael@0 | 13237 | { |
michael@0 | 13238 | ErrorResult rv; |
michael@0 | 13239 | Restore(rv); |
michael@0 | 13240 | return rv.ErrorCode(); |
michael@0 | 13241 | } |
michael@0 | 13242 | |
michael@0 | 13243 | void |
michael@0 | 13244 | nsGlobalWindow::Restore(ErrorResult& aError) |
michael@0 | 13245 | { |
michael@0 | 13246 | nsCOMPtr<nsIWidget> widget = GetMainWidget(); |
michael@0 | 13247 | |
michael@0 | 13248 | if (widget) { |
michael@0 | 13249 | aError = widget->SetSizeMode(nsSizeMode_Normal); |
michael@0 | 13250 | } |
michael@0 | 13251 | } |
michael@0 | 13252 | |
michael@0 | 13253 | NS_IMETHODIMP |
michael@0 | 13254 | nsGlobalChromeWindow::GetAttention() |
michael@0 | 13255 | { |
michael@0 | 13256 | ErrorResult rv; |
michael@0 | 13257 | GetAttention(rv); |
michael@0 | 13258 | return rv.ErrorCode(); |
michael@0 | 13259 | } |
michael@0 | 13260 | |
michael@0 | 13261 | void |
michael@0 | 13262 | nsGlobalWindow::GetAttention(ErrorResult& aResult) |
michael@0 | 13263 | { |
michael@0 | 13264 | return GetAttentionWithCycleCount(-1, aResult); |
michael@0 | 13265 | } |
michael@0 | 13266 | |
michael@0 | 13267 | NS_IMETHODIMP |
michael@0 | 13268 | nsGlobalChromeWindow::GetAttentionWithCycleCount(int32_t aCycleCount) |
michael@0 | 13269 | { |
michael@0 | 13270 | ErrorResult rv; |
michael@0 | 13271 | GetAttentionWithCycleCount(aCycleCount, rv); |
michael@0 | 13272 | return rv.ErrorCode(); |
michael@0 | 13273 | } |
michael@0 | 13274 | |
michael@0 | 13275 | void |
michael@0 | 13276 | nsGlobalWindow::GetAttentionWithCycleCount(int32_t aCycleCount, |
michael@0 | 13277 | ErrorResult& aError) |
michael@0 | 13278 | { |
michael@0 | 13279 | nsCOMPtr<nsIWidget> widget = GetMainWidget(); |
michael@0 | 13280 | |
michael@0 | 13281 | if (widget) { |
michael@0 | 13282 | aError = widget->GetAttention(aCycleCount); |
michael@0 | 13283 | } |
michael@0 | 13284 | } |
michael@0 | 13285 | |
michael@0 | 13286 | NS_IMETHODIMP |
michael@0 | 13287 | nsGlobalChromeWindow::BeginWindowMove(nsIDOMEvent *aMouseDownEvent, nsIDOMElement* aPanel) |
michael@0 | 13288 | { |
michael@0 | 13289 | NS_ENSURE_TRUE(aMouseDownEvent, NS_ERROR_FAILURE); |
michael@0 | 13290 | Event* mouseDownEvent = aMouseDownEvent->InternalDOMEvent(); |
michael@0 | 13291 | NS_ENSURE_TRUE(mouseDownEvent, NS_ERROR_FAILURE); |
michael@0 | 13292 | |
michael@0 | 13293 | nsCOMPtr<Element> panel = do_QueryInterface(aPanel); |
michael@0 | 13294 | NS_ENSURE_TRUE(panel || !aPanel, NS_ERROR_FAILURE); |
michael@0 | 13295 | |
michael@0 | 13296 | ErrorResult rv; |
michael@0 | 13297 | BeginWindowMove(*mouseDownEvent, panel, rv); |
michael@0 | 13298 | return rv.ErrorCode(); |
michael@0 | 13299 | } |
michael@0 | 13300 | |
michael@0 | 13301 | void |
michael@0 | 13302 | nsGlobalWindow::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel, |
michael@0 | 13303 | ErrorResult& aError) |
michael@0 | 13304 | { |
michael@0 | 13305 | nsCOMPtr<nsIWidget> widget; |
michael@0 | 13306 | |
michael@0 | 13307 | // if a panel was supplied, use its widget instead. |
michael@0 | 13308 | #ifdef MOZ_XUL |
michael@0 | 13309 | if (aPanel) { |
michael@0 | 13310 | nsIFrame* frame = aPanel->GetPrimaryFrame(); |
michael@0 | 13311 | if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame) { |
michael@0 | 13312 | return; |
michael@0 | 13313 | } |
michael@0 | 13314 | |
michael@0 | 13315 | widget = (static_cast<nsMenuPopupFrame*>(frame))->GetWidget(); |
michael@0 | 13316 | } |
michael@0 | 13317 | else { |
michael@0 | 13318 | #endif |
michael@0 | 13319 | widget = GetMainWidget(); |
michael@0 | 13320 | #ifdef MOZ_XUL |
michael@0 | 13321 | } |
michael@0 | 13322 | #endif |
michael@0 | 13323 | |
michael@0 | 13324 | if (!widget) { |
michael@0 | 13325 | return; |
michael@0 | 13326 | } |
michael@0 | 13327 | |
michael@0 | 13328 | WidgetMouseEvent* mouseEvent = |
michael@0 | 13329 | aMouseDownEvent.GetInternalNSEvent()->AsMouseEvent(); |
michael@0 | 13330 | if (!mouseEvent || mouseEvent->eventStructType != NS_MOUSE_EVENT) { |
michael@0 | 13331 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13332 | return; |
michael@0 | 13333 | } |
michael@0 | 13334 | |
michael@0 | 13335 | aError = widget->BeginMoveDrag(mouseEvent); |
michael@0 | 13336 | } |
michael@0 | 13337 | |
michael@0 | 13338 | //Note: This call will lock the cursor, it will not change as it moves. |
michael@0 | 13339 | //To unlock, the cursor must be set back to CURSOR_AUTO. |
michael@0 | 13340 | NS_IMETHODIMP |
michael@0 | 13341 | nsGlobalChromeWindow::SetCursor(const nsAString& aCursor) |
michael@0 | 13342 | { |
michael@0 | 13343 | ErrorResult rv; |
michael@0 | 13344 | SetCursor(aCursor, rv); |
michael@0 | 13345 | return rv.ErrorCode(); |
michael@0 | 13346 | } |
michael@0 | 13347 | |
michael@0 | 13348 | void |
michael@0 | 13349 | nsGlobalWindow::SetCursor(const nsAString& aCursor, ErrorResult& aError) |
michael@0 | 13350 | { |
michael@0 | 13351 | FORWARD_TO_OUTER_OR_THROW(SetCursor, (aCursor, aError), aError, ); |
michael@0 | 13352 | |
michael@0 | 13353 | int32_t cursor; |
michael@0 | 13354 | |
michael@0 | 13355 | if (aCursor.EqualsLiteral("auto")) |
michael@0 | 13356 | cursor = NS_STYLE_CURSOR_AUTO; |
michael@0 | 13357 | else { |
michael@0 | 13358 | nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aCursor); |
michael@0 | 13359 | if (eCSSKeyword_UNKNOWN == keyword || |
michael@0 | 13360 | !nsCSSProps::FindKeyword(keyword, nsCSSProps::kCursorKTable, cursor)) { |
michael@0 | 13361 | return; |
michael@0 | 13362 | } |
michael@0 | 13363 | } |
michael@0 | 13364 | |
michael@0 | 13365 | nsRefPtr<nsPresContext> presContext; |
michael@0 | 13366 | if (mDocShell) { |
michael@0 | 13367 | mDocShell->GetPresContext(getter_AddRefs(presContext)); |
michael@0 | 13368 | } |
michael@0 | 13369 | |
michael@0 | 13370 | if (presContext) { |
michael@0 | 13371 | // Need root widget. |
michael@0 | 13372 | nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); |
michael@0 | 13373 | if (!presShell) { |
michael@0 | 13374 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13375 | return; |
michael@0 | 13376 | } |
michael@0 | 13377 | |
michael@0 | 13378 | nsViewManager* vm = presShell->GetViewManager(); |
michael@0 | 13379 | if (!vm) { |
michael@0 | 13380 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13381 | return; |
michael@0 | 13382 | } |
michael@0 | 13383 | |
michael@0 | 13384 | nsView* rootView = vm->GetRootView(); |
michael@0 | 13385 | if (!rootView) { |
michael@0 | 13386 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13387 | return; |
michael@0 | 13388 | } |
michael@0 | 13389 | |
michael@0 | 13390 | nsIWidget* widget = rootView->GetNearestWidget(nullptr); |
michael@0 | 13391 | if (!widget) { |
michael@0 | 13392 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13393 | return; |
michael@0 | 13394 | } |
michael@0 | 13395 | |
michael@0 | 13396 | // Call esm and set cursor. |
michael@0 | 13397 | aError = presContext->EventStateManager()->SetCursor(cursor, nullptr, |
michael@0 | 13398 | false, 0.0f, 0.0f, |
michael@0 | 13399 | widget, true); |
michael@0 | 13400 | } |
michael@0 | 13401 | } |
michael@0 | 13402 | |
michael@0 | 13403 | NS_IMETHODIMP |
michael@0 | 13404 | nsGlobalChromeWindow::GetBrowserDOMWindow(nsIBrowserDOMWindow **aBrowserWindow) |
michael@0 | 13405 | { |
michael@0 | 13406 | ErrorResult rv; |
michael@0 | 13407 | NS_IF_ADDREF(*aBrowserWindow = GetBrowserDOMWindow(rv)); |
michael@0 | 13408 | return rv.ErrorCode(); |
michael@0 | 13409 | } |
michael@0 | 13410 | |
michael@0 | 13411 | nsIBrowserDOMWindow* |
michael@0 | 13412 | nsGlobalWindow::GetBrowserDOMWindow(ErrorResult& aError) |
michael@0 | 13413 | { |
michael@0 | 13414 | FORWARD_TO_OUTER_OR_THROW(GetBrowserDOMWindow, (aError), aError, nullptr); |
michael@0 | 13415 | |
michael@0 | 13416 | MOZ_ASSERT(IsChromeWindow()); |
michael@0 | 13417 | return static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow; |
michael@0 | 13418 | } |
michael@0 | 13419 | |
michael@0 | 13420 | NS_IMETHODIMP |
michael@0 | 13421 | nsGlobalChromeWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow *aBrowserWindow) |
michael@0 | 13422 | { |
michael@0 | 13423 | ErrorResult rv; |
michael@0 | 13424 | SetBrowserDOMWindow(aBrowserWindow, rv); |
michael@0 | 13425 | return rv.ErrorCode(); |
michael@0 | 13426 | } |
michael@0 | 13427 | |
michael@0 | 13428 | void |
michael@0 | 13429 | nsGlobalWindow::SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow, |
michael@0 | 13430 | ErrorResult& aError) |
michael@0 | 13431 | { |
michael@0 | 13432 | FORWARD_TO_OUTER_OR_THROW(SetBrowserDOMWindow, (aBrowserWindow, aError), |
michael@0 | 13433 | aError, ); |
michael@0 | 13434 | MOZ_ASSERT(IsChromeWindow()); |
michael@0 | 13435 | static_cast<nsGlobalChromeWindow*>(this)->mBrowserDOMWindow = aBrowserWindow; |
michael@0 | 13436 | } |
michael@0 | 13437 | |
michael@0 | 13438 | NS_IMETHODIMP |
michael@0 | 13439 | nsGlobalChromeWindow::NotifyDefaultButtonLoaded(nsIDOMElement* aDefaultButton) |
michael@0 | 13440 | { |
michael@0 | 13441 | nsCOMPtr<Element> defaultButton = do_QueryInterface(aDefaultButton); |
michael@0 | 13442 | NS_ENSURE_ARG(defaultButton); |
michael@0 | 13443 | |
michael@0 | 13444 | ErrorResult rv; |
michael@0 | 13445 | NotifyDefaultButtonLoaded(*defaultButton, rv); |
michael@0 | 13446 | return rv.ErrorCode(); |
michael@0 | 13447 | } |
michael@0 | 13448 | |
michael@0 | 13449 | void |
michael@0 | 13450 | nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton, |
michael@0 | 13451 | ErrorResult& aError) |
michael@0 | 13452 | { |
michael@0 | 13453 | #ifdef MOZ_XUL |
michael@0 | 13454 | // Don't snap to a disabled button. |
michael@0 | 13455 | nsCOMPtr<nsIDOMXULControlElement> xulControl = |
michael@0 | 13456 | do_QueryInterface(&aDefaultButton); |
michael@0 | 13457 | if (!xulControl) { |
michael@0 | 13458 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13459 | return; |
michael@0 | 13460 | } |
michael@0 | 13461 | bool disabled; |
michael@0 | 13462 | aError = xulControl->GetDisabled(&disabled); |
michael@0 | 13463 | if (aError.Failed() || disabled) { |
michael@0 | 13464 | return; |
michael@0 | 13465 | } |
michael@0 | 13466 | |
michael@0 | 13467 | // Get the button rect in screen coordinates. |
michael@0 | 13468 | nsIFrame *frame = aDefaultButton.GetPrimaryFrame(); |
michael@0 | 13469 | if (!frame) { |
michael@0 | 13470 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13471 | return; |
michael@0 | 13472 | } |
michael@0 | 13473 | nsIntRect buttonRect = frame->GetScreenRect(); |
michael@0 | 13474 | |
michael@0 | 13475 | // Get the widget rect in screen coordinates. |
michael@0 | 13476 | nsIWidget *widget = GetNearestWidget(); |
michael@0 | 13477 | if (!widget) { |
michael@0 | 13478 | aError.Throw(NS_ERROR_FAILURE); |
michael@0 | 13479 | return; |
michael@0 | 13480 | } |
michael@0 | 13481 | nsIntRect widgetRect; |
michael@0 | 13482 | aError = widget->GetScreenBounds(widgetRect); |
michael@0 | 13483 | if (aError.Failed()) { |
michael@0 | 13484 | return; |
michael@0 | 13485 | } |
michael@0 | 13486 | |
michael@0 | 13487 | // Convert the buttonRect coordinates from screen to the widget. |
michael@0 | 13488 | buttonRect -= widgetRect.TopLeft(); |
michael@0 | 13489 | nsresult rv = widget->OnDefaultButtonLoaded(buttonRect); |
michael@0 | 13490 | if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) { |
michael@0 | 13491 | aError.Throw(rv); |
michael@0 | 13492 | } |
michael@0 | 13493 | #else |
michael@0 | 13494 | aError.Throw(NS_ERROR_NOT_IMPLEMENTED); |
michael@0 | 13495 | #endif |
michael@0 | 13496 | } |
michael@0 | 13497 | |
michael@0 | 13498 | NS_IMETHODIMP |
michael@0 | 13499 | nsGlobalChromeWindow::GetMessageManager(nsIMessageBroadcaster** aManager) |
michael@0 | 13500 | { |
michael@0 | 13501 | ErrorResult rv; |
michael@0 | 13502 | NS_IF_ADDREF(*aManager = GetMessageManager(rv)); |
michael@0 | 13503 | return rv.ErrorCode(); |
michael@0 | 13504 | } |
michael@0 | 13505 | |
michael@0 | 13506 | nsIMessageBroadcaster* |
michael@0 | 13507 | nsGlobalWindow::GetMessageManager(ErrorResult& aError) |
michael@0 | 13508 | { |
michael@0 | 13509 | FORWARD_TO_INNER_OR_THROW(GetMessageManager, (aError), aError, nullptr); |
michael@0 | 13510 | MOZ_ASSERT(IsChromeWindow()); |
michael@0 | 13511 | nsGlobalChromeWindow* myself = static_cast<nsGlobalChromeWindow*>(this); |
michael@0 | 13512 | if (!myself->mMessageManager) { |
michael@0 | 13513 | nsIScriptContext* scx = GetContextInternal(); |
michael@0 | 13514 | if (NS_WARN_IF(!scx || !(scx->GetNativeContext()))) { |
michael@0 | 13515 | aError.Throw(NS_ERROR_UNEXPECTED); |
michael@0 | 13516 | return nullptr; |
michael@0 | 13517 | } |
michael@0 | 13518 | |
michael@0 | 13519 | nsCOMPtr<nsIMessageBroadcaster> globalMM = |
michael@0 | 13520 | do_GetService("@mozilla.org/globalmessagemanager;1"); |
michael@0 | 13521 | myself->mMessageManager = |
michael@0 | 13522 | new nsFrameMessageManager(nullptr, |
michael@0 | 13523 | static_cast<nsFrameMessageManager*>(globalMM.get()), |
michael@0 | 13524 | MM_CHROME | MM_BROADCASTER); |
michael@0 | 13525 | } |
michael@0 | 13526 | return myself->mMessageManager; |
michael@0 | 13527 | } |
michael@0 | 13528 | |
michael@0 | 13529 | // nsGlobalModalWindow implementation |
michael@0 | 13530 | |
michael@0 | 13531 | // QueryInterface implementation for nsGlobalModalWindow |
michael@0 | 13532 | DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow) |
michael@0 | 13533 | |
michael@0 | 13534 | NS_INTERFACE_MAP_BEGIN(nsGlobalModalWindow) |
michael@0 | 13535 | NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow) |
michael@0 | 13536 | NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow) |
michael@0 | 13537 | NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow) |
michael@0 | 13538 | |
michael@0 | 13539 | NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow) |
michael@0 | 13540 | NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow) |
michael@0 | 13541 | |
michael@0 | 13542 | |
michael@0 | 13543 | void |
michael@0 | 13544 | nsGlobalWindow::GetDialogArguments(JSContext* aCx, |
michael@0 | 13545 | JS::MutableHandle<JS::Value> aRetval, |
michael@0 | 13546 | ErrorResult& aError) |
michael@0 | 13547 | { |
michael@0 | 13548 | FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aRetval, aError), |
michael@0 | 13549 | aError, ); |
michael@0 | 13550 | |
michael@0 | 13551 | MOZ_ASSERT(IsModalContentWindow(), |
michael@0 | 13552 | "This should only be called on modal windows!"); |
michael@0 | 13553 | |
michael@0 | 13554 | // This does an internal origin check, and returns undefined if the subject |
michael@0 | 13555 | // does not subsumes the origin of the arguments. |
michael@0 | 13556 | JS::Rooted<JSObject*> wrapper(aCx, GetWrapper()); |
michael@0 | 13557 | JSAutoCompartment ac(aCx, wrapper); |
michael@0 | 13558 | mDialogArguments->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(), |
michael@0 | 13559 | aRetval, aError); |
michael@0 | 13560 | } |
michael@0 | 13561 | |
michael@0 | 13562 | NS_IMETHODIMP |
michael@0 | 13563 | nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments) |
michael@0 | 13564 | { |
michael@0 | 13565 | FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments), |
michael@0 | 13566 | NS_ERROR_NOT_INITIALIZED); |
michael@0 | 13567 | |
michael@0 | 13568 | // This does an internal origin check, and returns undefined if the subject |
michael@0 | 13569 | // does not subsumes the origin of the arguments. |
michael@0 | 13570 | return mDialogArguments->Get(nsContentUtils::GetSubjectPrincipal(), aArguments); |
michael@0 | 13571 | } |
michael@0 | 13572 | |
michael@0 | 13573 | void |
michael@0 | 13574 | nsGlobalWindow::GetReturnValue(JSContext* aCx, |
michael@0 | 13575 | JS::MutableHandle<JS::Value> aReturnValue, |
michael@0 | 13576 | ErrorResult& aError) |
michael@0 | 13577 | { |
michael@0 | 13578 | FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aReturnValue, aError), |
michael@0 | 13579 | aError, ); |
michael@0 | 13580 | |
michael@0 | 13581 | MOZ_ASSERT(IsModalContentWindow(), |
michael@0 | 13582 | "This should only be called on modal windows!"); |
michael@0 | 13583 | |
michael@0 | 13584 | if (mReturnValue) { |
michael@0 | 13585 | JS::Rooted<JSObject*> wrapper(aCx, GetWrapper()); |
michael@0 | 13586 | JSAutoCompartment ac(aCx, wrapper); |
michael@0 | 13587 | mReturnValue->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(), |
michael@0 | 13588 | aReturnValue, aError); |
michael@0 | 13589 | } else { |
michael@0 | 13590 | aReturnValue.setUndefined(); |
michael@0 | 13591 | } |
michael@0 | 13592 | } |
michael@0 | 13593 | |
michael@0 | 13594 | NS_IMETHODIMP |
michael@0 | 13595 | nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal) |
michael@0 | 13596 | { |
michael@0 | 13597 | FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK); |
michael@0 | 13598 | |
michael@0 | 13599 | nsCOMPtr<nsIVariant> result; |
michael@0 | 13600 | if (!mReturnValue) { |
michael@0 | 13601 | nsCOMPtr<nsIVariant> variant = CreateVoidVariant(); |
michael@0 | 13602 | variant.forget(aRetVal); |
michael@0 | 13603 | return NS_OK; |
michael@0 | 13604 | } |
michael@0 | 13605 | return mReturnValue->Get(nsContentUtils::GetSubjectPrincipal(), aRetVal); |
michael@0 | 13606 | } |
michael@0 | 13607 | |
michael@0 | 13608 | void |
michael@0 | 13609 | nsGlobalWindow::SetReturnValue(JSContext* aCx, |
michael@0 | 13610 | JS::Handle<JS::Value> aReturnValue, |
michael@0 | 13611 | ErrorResult& aError) |
michael@0 | 13612 | { |
michael@0 | 13613 | FORWARD_TO_OUTER_OR_THROW(SetReturnValue, (aCx, aReturnValue, aError), |
michael@0 | 13614 | aError, ); |
michael@0 | 13615 | |
michael@0 | 13616 | MOZ_ASSERT(IsModalContentWindow(), |
michael@0 | 13617 | "This should only be called on modal windows!"); |
michael@0 | 13618 | |
michael@0 | 13619 | nsCOMPtr<nsIVariant> returnValue; |
michael@0 | 13620 | aError = |
michael@0 | 13621 | nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue, |
michael@0 | 13622 | getter_AddRefs(returnValue)); |
michael@0 | 13623 | if (!aError.Failed()) { |
michael@0 | 13624 | mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), |
michael@0 | 13625 | returnValue); |
michael@0 | 13626 | } |
michael@0 | 13627 | } |
michael@0 | 13628 | |
michael@0 | 13629 | NS_IMETHODIMP |
michael@0 | 13630 | nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal) |
michael@0 | 13631 | { |
michael@0 | 13632 | FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK); |
michael@0 | 13633 | |
michael@0 | 13634 | mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), |
michael@0 | 13635 | aRetVal); |
michael@0 | 13636 | return NS_OK; |
michael@0 | 13637 | } |
michael@0 | 13638 | |
michael@0 | 13639 | /* static */ |
michael@0 | 13640 | bool |
michael@0 | 13641 | nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal) |
michael@0 | 13642 | { |
michael@0 | 13643 | // For now, have to deal with XPConnect objects here. |
michael@0 | 13644 | return xpc::WindowOrNull(aGlobal)->IsModalContentWindow(); |
michael@0 | 13645 | } |
michael@0 | 13646 | |
michael@0 | 13647 | NS_IMETHODIMP |
michael@0 | 13648 | nsGlobalWindow::GetConsole(JSContext* aCx, |
michael@0 | 13649 | JS::MutableHandle<JS::Value> aConsole) |
michael@0 | 13650 | { |
michael@0 | 13651 | ErrorResult rv; |
michael@0 | 13652 | nsRefPtr<Console> console = GetConsole(rv); |
michael@0 | 13653 | if (rv.Failed()) { |
michael@0 | 13654 | return rv.ErrorCode(); |
michael@0 | 13655 | } |
michael@0 | 13656 | |
michael@0 | 13657 | if (!WrapNewBindingObject(aCx, console, aConsole)) { |
michael@0 | 13658 | return NS_ERROR_FAILURE; |
michael@0 | 13659 | } |
michael@0 | 13660 | |
michael@0 | 13661 | return NS_OK; |
michael@0 | 13662 | } |
michael@0 | 13663 | |
michael@0 | 13664 | NS_IMETHODIMP |
michael@0 | 13665 | nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue) |
michael@0 | 13666 | { |
michael@0 | 13667 | JS::Rooted<JSObject*> thisObj(aCx, GetWrapper()); |
michael@0 | 13668 | if (!thisObj) { |
michael@0 | 13669 | return NS_ERROR_UNEXPECTED; |
michael@0 | 13670 | } |
michael@0 | 13671 | |
michael@0 | 13672 | if (!JS_WrapObject(aCx, &thisObj) || |
michael@0 | 13673 | !JS_DefineProperty(aCx, thisObj, "console", aValue, |
michael@0 | 13674 | JSPROP_ENUMERATE, JS_PropertyStub, |
michael@0 | 13675 | JS_StrictPropertyStub)) { |
michael@0 | 13676 | return NS_ERROR_FAILURE; |
michael@0 | 13677 | } |
michael@0 | 13678 | |
michael@0 | 13679 | return NS_OK; |
michael@0 | 13680 | } |
michael@0 | 13681 | |
michael@0 | 13682 | Console* |
michael@0 | 13683 | nsGlobalWindow::GetConsole(ErrorResult& aRv) |
michael@0 | 13684 | { |
michael@0 | 13685 | FORWARD_TO_INNER_OR_THROW(GetConsole, (aRv), aRv, nullptr); |
michael@0 | 13686 | |
michael@0 | 13687 | if (!mConsole) { |
michael@0 | 13688 | mConsole = new Console(this); |
michael@0 | 13689 | } |
michael@0 | 13690 | |
michael@0 | 13691 | return mConsole; |
michael@0 | 13692 | } |
michael@0 | 13693 | |
michael@0 | 13694 | already_AddRefed<External> |
michael@0 | 13695 | nsGlobalWindow::GetExternal(ErrorResult& aRv) |
michael@0 | 13696 | { |
michael@0 | 13697 | FORWARD_TO_INNER_OR_THROW(GetExternal, (aRv), aRv, nullptr); |
michael@0 | 13698 | |
michael@0 | 13699 | #ifdef HAVE_SIDEBAR |
michael@0 | 13700 | if (!mExternal) { |
michael@0 | 13701 | AutoJSContext cx; |
michael@0 | 13702 | JS::Rooted<JSObject*> jsImplObj(cx); |
michael@0 | 13703 | ConstructJSImplementation(cx, "@mozilla.org/sidebar;1", |
michael@0 | 13704 | this, &jsImplObj, aRv); |
michael@0 | 13705 | if (aRv.Failed()) { |
michael@0 | 13706 | return nullptr; |
michael@0 | 13707 | } |
michael@0 | 13708 | mExternal = new External(jsImplObj, this); |
michael@0 | 13709 | } |
michael@0 | 13710 | |
michael@0 | 13711 | nsRefPtr<External> external = static_cast<External*>(mExternal.get()); |
michael@0 | 13712 | return external.forget(); |
michael@0 | 13713 | #else |
michael@0 | 13714 | aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); |
michael@0 | 13715 | return nullptr; |
michael@0 | 13716 | #endif |
michael@0 | 13717 | } |
michael@0 | 13718 | |
michael@0 | 13719 | void |
michael@0 | 13720 | nsGlobalWindow::GetSidebar(OwningExternalOrWindowProxy& aResult, |
michael@0 | 13721 | ErrorResult& aRv) |
michael@0 | 13722 | { |
michael@0 | 13723 | FORWARD_TO_INNER_OR_THROW(GetSidebar, (aResult, aRv), aRv, ); |
michael@0 | 13724 | |
michael@0 | 13725 | #ifdef HAVE_SIDEBAR |
michael@0 | 13726 | // First check for a named frame named "sidebar" |
michael@0 | 13727 | nsCOMPtr<nsIDOMWindow> domWindow = GetChildWindow(NS_LITERAL_STRING("sidebar")); |
michael@0 | 13728 | if (domWindow) { |
michael@0 | 13729 | aResult.SetAsWindowProxy() = domWindow.forget(); |
michael@0 | 13730 | return; |
michael@0 | 13731 | } |
michael@0 | 13732 | |
michael@0 | 13733 | nsRefPtr<External> external = GetExternal(aRv); |
michael@0 | 13734 | if (external) { |
michael@0 | 13735 | aResult.SetAsExternal() = external; |
michael@0 | 13736 | } |
michael@0 | 13737 | #else |
michael@0 | 13738 | aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); |
michael@0 | 13739 | #endif |
michael@0 | 13740 | } |
michael@0 | 13741 | |
michael@0 | 13742 | /* static */ |
michael@0 | 13743 | bool |
michael@0 | 13744 | nsGlobalWindow::WindowOnWebIDL(JSContext* aCx, JSObject* aObj) |
michael@0 | 13745 | { |
michael@0 | 13746 | DebugOnly<nsGlobalWindow*> win; |
michael@0 | 13747 | MOZ_ASSERT_IF(IsDOMObject(aObj), |
michael@0 | 13748 | NS_SUCCEEDED(UNWRAP_OBJECT(Window, aObj, win))); |
michael@0 | 13749 | |
michael@0 | 13750 | return IsDOMObject(aObj); |
michael@0 | 13751 | } |
michael@0 | 13752 | |
michael@0 | 13753 | #ifdef MOZ_B2G |
michael@0 | 13754 | void |
michael@0 | 13755 | nsGlobalWindow::EnableNetworkEvent(uint32_t aType) |
michael@0 | 13756 | { |
michael@0 | 13757 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 13758 | |
michael@0 | 13759 | nsCOMPtr<nsIPermissionManager> permMgr = |
michael@0 | 13760 | do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
michael@0 | 13761 | if (!permMgr) { |
michael@0 | 13762 | NS_ERROR("No PermissionManager available!"); |
michael@0 | 13763 | return; |
michael@0 | 13764 | } |
michael@0 | 13765 | |
michael@0 | 13766 | uint32_t permission = nsIPermissionManager::DENY_ACTION; |
michael@0 | 13767 | permMgr->TestExactPermissionFromPrincipal(GetPrincipal(), "network-events", |
michael@0 | 13768 | &permission); |
michael@0 | 13769 | |
michael@0 | 13770 | if (permission != nsIPermissionManager::ALLOW_ACTION) { |
michael@0 | 13771 | return; |
michael@0 | 13772 | } |
michael@0 | 13773 | |
michael@0 | 13774 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 13775 | if (!os) { |
michael@0 | 13776 | NS_ERROR("ObserverService should be available!"); |
michael@0 | 13777 | return; |
michael@0 | 13778 | } |
michael@0 | 13779 | |
michael@0 | 13780 | switch (aType) { |
michael@0 | 13781 | case NS_NETWORK_UPLOAD_EVENT: |
michael@0 | 13782 | if (!mNetworkUploadObserverEnabled) { |
michael@0 | 13783 | mNetworkUploadObserverEnabled = true; |
michael@0 | 13784 | os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC, false); |
michael@0 | 13785 | } |
michael@0 | 13786 | break; |
michael@0 | 13787 | case NS_NETWORK_DOWNLOAD_EVENT: |
michael@0 | 13788 | if (!mNetworkDownloadObserverEnabled) { |
michael@0 | 13789 | mNetworkDownloadObserverEnabled = true; |
michael@0 | 13790 | os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, false); |
michael@0 | 13791 | } |
michael@0 | 13792 | break; |
michael@0 | 13793 | } |
michael@0 | 13794 | } |
michael@0 | 13795 | |
michael@0 | 13796 | void |
michael@0 | 13797 | nsGlobalWindow::DisableNetworkEvent(uint32_t aType) |
michael@0 | 13798 | { |
michael@0 | 13799 | MOZ_ASSERT(IsInnerWindow()); |
michael@0 | 13800 | |
michael@0 | 13801 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
michael@0 | 13802 | if (!os) { |
michael@0 | 13803 | return; |
michael@0 | 13804 | } |
michael@0 | 13805 | |
michael@0 | 13806 | switch (aType) { |
michael@0 | 13807 | case NS_NETWORK_UPLOAD_EVENT: |
michael@0 | 13808 | if (mNetworkUploadObserverEnabled) { |
michael@0 | 13809 | mNetworkUploadObserverEnabled = false; |
michael@0 | 13810 | os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC); |
michael@0 | 13811 | } |
michael@0 | 13812 | break; |
michael@0 | 13813 | case NS_NETWORK_DOWNLOAD_EVENT: |
michael@0 | 13814 | if (mNetworkDownloadObserverEnabled) { |
michael@0 | 13815 | mNetworkDownloadObserverEnabled = false; |
michael@0 | 13816 | os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC); |
michael@0 | 13817 | } |
michael@0 | 13818 | break; |
michael@0 | 13819 | } |
michael@0 | 13820 | } |
michael@0 | 13821 | #endif // MOZ_B2G |
michael@0 | 13822 | |
michael@0 | 13823 | #define EVENT(name_, id_, type_, struct_) \ |
michael@0 | 13824 | NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \ |
michael@0 | 13825 | JS::MutableHandle<JS::Value> vp) { \ |
michael@0 | 13826 | EventHandlerNonNull* h = GetOn##name_(); \ |
michael@0 | 13827 | vp.setObjectOrNull(h ? h->Callable().get() : nullptr); \ |
michael@0 | 13828 | return NS_OK; \ |
michael@0 | 13829 | } \ |
michael@0 | 13830 | NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \ |
michael@0 | 13831 | JS::Handle<JS::Value> v) { \ |
michael@0 | 13832 | nsRefPtr<EventHandlerNonNull> handler; \ |
michael@0 | 13833 | JS::Rooted<JSObject*> callable(cx); \ |
michael@0 | 13834 | if (v.isObject() && \ |
michael@0 | 13835 | JS_ObjectIsCallable(cx, callable = &v.toObject())) { \ |
michael@0 | 13836 | handler = new EventHandlerNonNull(callable, GetIncumbentGlobal()); \ |
michael@0 | 13837 | } \ |
michael@0 | 13838 | SetOn##name_(handler); \ |
michael@0 | 13839 | return NS_OK; \ |
michael@0 | 13840 | } |
michael@0 | 13841 | #define ERROR_EVENT(name_, id_, type_, struct_) \ |
michael@0 | 13842 | NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \ |
michael@0 | 13843 | JS::MutableHandle<JS::Value> vp) { \ |
michael@0 | 13844 | EventListenerManager *elm = GetExistingListenerManager(); \ |
michael@0 | 13845 | if (elm) { \ |
michael@0 | 13846 | OnErrorEventHandlerNonNull* h = elm->GetOnErrorEventHandler(); \ |
michael@0 | 13847 | if (h) { \ |
michael@0 | 13848 | vp.setObject(*h->Callable()); \ |
michael@0 | 13849 | return NS_OK; \ |
michael@0 | 13850 | } \ |
michael@0 | 13851 | } \ |
michael@0 | 13852 | vp.setNull(); \ |
michael@0 | 13853 | return NS_OK; \ |
michael@0 | 13854 | } \ |
michael@0 | 13855 | NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \ |
michael@0 | 13856 | JS::Handle<JS::Value> v) { \ |
michael@0 | 13857 | EventListenerManager *elm = GetOrCreateListenerManager(); \ |
michael@0 | 13858 | if (!elm) { \ |
michael@0 | 13859 | return NS_ERROR_OUT_OF_MEMORY; \ |
michael@0 | 13860 | } \ |
michael@0 | 13861 | \ |
michael@0 | 13862 | nsRefPtr<OnErrorEventHandlerNonNull> handler; \ |
michael@0 | 13863 | JS::Rooted<JSObject*> callable(cx); \ |
michael@0 | 13864 | if (v.isObject() && \ |
michael@0 | 13865 | JS_ObjectIsCallable(cx, callable = &v.toObject())) { \ |
michael@0 | 13866 | handler = new OnErrorEventHandlerNonNull(callable, GetIncumbentGlobal()); \ |
michael@0 | 13867 | } \ |
michael@0 | 13868 | elm->SetEventHandler(handler); \ |
michael@0 | 13869 | return NS_OK; \ |
michael@0 | 13870 | } |
michael@0 | 13871 | #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_) \ |
michael@0 | 13872 | NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx, \ |
michael@0 | 13873 | JS::MutableHandle<JS::Value> vp) { \ |
michael@0 | 13874 | EventListenerManager *elm = GetExistingListenerManager(); \ |
michael@0 | 13875 | if (elm) { \ |
michael@0 | 13876 | OnBeforeUnloadEventHandlerNonNull* h = \ |
michael@0 | 13877 | elm->GetOnBeforeUnloadEventHandler(); \ |
michael@0 | 13878 | if (h) { \ |
michael@0 | 13879 | vp.setObject(*h->Callable()); \ |
michael@0 | 13880 | return NS_OK; \ |
michael@0 | 13881 | } \ |
michael@0 | 13882 | } \ |
michael@0 | 13883 | vp.setNull(); \ |
michael@0 | 13884 | return NS_OK; \ |
michael@0 | 13885 | } \ |
michael@0 | 13886 | NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx, \ |
michael@0 | 13887 | JS::Handle<JS::Value> v) { \ |
michael@0 | 13888 | EventListenerManager *elm = GetOrCreateListenerManager(); \ |
michael@0 | 13889 | if (!elm) { \ |
michael@0 | 13890 | return NS_ERROR_OUT_OF_MEMORY; \ |
michael@0 | 13891 | } \ |
michael@0 | 13892 | \ |
michael@0 | 13893 | nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler; \ |
michael@0 | 13894 | JS::Rooted<JSObject*> callable(cx); \ |
michael@0 | 13895 | if (v.isObject() && \ |
michael@0 | 13896 | JS_ObjectIsCallable(cx, callable = &v.toObject())) { \ |
michael@0 | 13897 | handler = new OnBeforeUnloadEventHandlerNonNull(callable, GetIncumbentGlobal()); \ |
michael@0 | 13898 | } \ |
michael@0 | 13899 | elm->SetEventHandler(handler); \ |
michael@0 | 13900 | return NS_OK; \ |
michael@0 | 13901 | } |
michael@0 | 13902 | #define WINDOW_ONLY_EVENT EVENT |
michael@0 | 13903 | #define TOUCH_EVENT EVENT |
michael@0 | 13904 | #include "mozilla/EventNameList.h" |
michael@0 | 13905 | #undef TOUCH_EVENT |
michael@0 | 13906 | #undef WINDOW_ONLY_EVENT |
michael@0 | 13907 | #undef BEFOREUNLOAD_EVENT |
michael@0 | 13908 | #undef ERROR_EVENT |
michael@0 | 13909 | #undef EVENT |
michael@0 | 13910 | |
michael@0 | 13911 | #ifdef _WINDOWS_ |
michael@0 | 13912 | #error "Never include windows.h in this file!" |
michael@0 | 13913 | #endif |