WindowDestroyedEvent.cpp (5253B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WindowDestroyedEvent.h" 8 9 #include "js/Wrapper.h" 10 #include "jsapi.h" 11 #include "mozilla/AppShutdown.h" 12 #include "mozilla/BasePrincipal.h" 13 #include "mozilla/Components.h" 14 #include "mozilla/ProfilerLabels.h" 15 #include "nsCOMPtr.h" 16 #include "nsContentUtils.h" 17 #include "nsFocusManager.h" 18 #include "nsGlobalWindowInner.h" 19 #include "nsGlobalWindowOuter.h" 20 #include "nsIAppStartup.h" 21 #include "nsIPrincipal.h" 22 #include "nsISupportsPrimitives.h" 23 #include "nsJSPrincipals.h" 24 #include "nsJSUtils.h" 25 #include "xpcpublic.h" 26 27 namespace mozilla { 28 29 struct BrowserCompartmentMatcher : public js::CompartmentFilter { 30 bool match(JS::Compartment* aC) const override { 31 return !xpc::MightBeWebContentCompartment(aC); 32 } 33 }; 34 35 WindowDestroyedEvent::WindowDestroyedEvent(nsGlobalWindowInner* aWindow, 36 uint64_t aID, const char* aTopic) 37 : mozilla::Runnable("WindowDestroyedEvent"), 38 mID(aID), 39 mPhase(Phase::Destroying), 40 mTopic(aTopic), 41 mIsInnerWindow(true) { 42 mWindow = do_GetWeakReference(aWindow); 43 } 44 45 WindowDestroyedEvent::WindowDestroyedEvent(nsGlobalWindowOuter* aWindow, 46 uint64_t aID, const char* aTopic) 47 : mozilla::Runnable("WindowDestroyedEvent"), 48 mID(aID), 49 mPhase(Phase::Destroying), 50 mTopic(aTopic), 51 mIsInnerWindow(false) { 52 mWindow = do_GetWeakReference(aWindow); 53 } 54 55 NS_IMETHODIMP 56 WindowDestroyedEvent::Run() { 57 AUTO_PROFILER_LABEL("WindowDestroyedEvent::Run", OTHER); 58 59 nsCOMPtr<nsPIDOMWindowOuter> nukedOuter; 60 61 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); 62 if (!observerService) { 63 return NS_OK; 64 } 65 66 nsCOMPtr<nsISupportsPRUint64> wrapper = 67 do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID); 68 if (wrapper) { 69 wrapper->SetData(mID); 70 observerService->NotifyObservers(wrapper, mTopic.get(), nullptr); 71 } 72 73 switch (mPhase) { 74 case Phase::Destroying: { 75 bool skipNukeCrossCompartment = false; 76 #ifndef DEBUG 77 skipNukeCrossCompartment = 78 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed); 79 #endif 80 81 if (!skipNukeCrossCompartment) { 82 // The compartment nuking phase might be too expensive, so do that 83 // part off of idle dispatch. 84 85 // For the compartment nuking phase, we dispatch either an 86 // inner-window-nuked or an outer-window-nuked notification. 87 // This will allow tests to wait for compartment nuking to happen. 88 if (mTopic.EqualsLiteral("inner-window-destroyed")) { 89 mTopic.AssignLiteral("inner-window-nuked"); 90 } else if (mTopic.EqualsLiteral("outer-window-destroyed")) { 91 mTopic.AssignLiteral("outer-window-nuked"); 92 } 93 mPhase = Phase::Nuking; 94 95 nsCOMPtr<nsIRunnable> copy(this); 96 NS_DispatchToCurrentThreadQueue(copy.forget(), 1000, 97 EventQueuePriority::Idle); 98 } 99 } break; 100 101 case Phase::Nuking: { 102 nsCOMPtr<nsISupports> window = do_QueryReferent(mWindow); 103 if (window) { 104 nsGlobalWindowInner* currentInner; 105 if (mIsInnerWindow) { 106 currentInner = nsGlobalWindowInner::FromSupports(window); 107 } else { 108 nsGlobalWindowOuter* outer = 109 nsGlobalWindowOuter::FromSupports(window); 110 currentInner = 111 nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow()); 112 nukedOuter = outer; 113 } 114 NS_ENSURE_TRUE(currentInner, NS_OK); 115 116 dom::AutoJSAPI jsapi; 117 jsapi.Init(); 118 JSContext* cx = jsapi.cx(); 119 JS::Rooted<JSObject*> obj(cx, currentInner->GetGlobalJSObject()); 120 if (obj && !js::IsSystemRealm(js::GetNonCCWObjectRealm(obj))) { 121 JS::Realm* realm = js::GetNonCCWObjectRealm(obj); 122 123 xpc::NukeJSStackFrames(realm); 124 125 nsCOMPtr<nsIPrincipal> pc = 126 nsJSPrincipals::get(JS::GetRealmPrincipals(realm)); 127 128 if (BasePrincipal::Cast(pc)->AddonPolicy()) { 129 // We want to nuke all references to the add-on realm. 130 xpc::NukeAllWrappersForRealm(cx, realm, 131 mIsInnerWindow 132 ? js::DontNukeWindowReferences 133 : js::NukeWindowReferences); 134 } else { 135 // We only want to nuke wrappers for the chrome->content case 136 js::NukeCrossCompartmentWrappers( 137 cx, BrowserCompartmentMatcher(), realm, 138 mIsInnerWindow ? js::DontNukeWindowReferences 139 : js::NukeWindowReferences, 140 js::NukeIncomingReferences); 141 } 142 } 143 } 144 } break; 145 } 146 147 if (nukedOuter) { 148 nsFocusManager* fm = nsFocusManager::GetFocusManager(); 149 if (fm) { 150 fm->WasNuked(nukedOuter); 151 } 152 } 153 154 return NS_OK; 155 } 156 157 } // namespace mozilla