CloseWatcher.cpp (4983B)
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 "CloseWatcher.h" 8 9 #include "mozilla/RefPtr.h" 10 #include "mozilla/dom/CloseWatcherBinding.h" 11 #include "mozilla/dom/CloseWatcherManager.h" 12 #include "mozilla/dom/Event.h" 13 #include "mozilla/dom/EventBinding.h" 14 #include "mozilla/dom/ToJSValue.h" 15 #include "mozilla/dom/WindowContext.h" 16 #include "nsGlobalWindowInner.h" 17 18 namespace mozilla::dom { 19 20 NS_IMPL_ISUPPORTS_INHERITED0(CloseWatcher, DOMEventTargetHelper) 21 22 already_AddRefed<CloseWatcher> CloseWatcher::Constructor( 23 const GlobalObject& aGlobal, const CloseWatcherOptions& aOptions, 24 ErrorResult& aRv) { 25 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 26 RefPtr<nsPIDOMWindowInner> window = global->GetAsInnerWindow(); 27 if (!window || !window->IsFullyActive()) { 28 aRv.ThrowInvalidStateError("The document is not fully active."); 29 return nullptr; 30 } 31 32 RefPtr<CloseWatcher> watcher = new CloseWatcher(window); 33 34 AbortSignal* signal = nullptr; 35 if (aOptions.mSignal.WasPassed()) { 36 signal = &aOptions.mSignal.Value(); 37 } 38 if (signal && signal->Aborted()) { 39 return watcher.forget(); 40 } 41 if (signal) { 42 watcher->Follow(signal); 43 } 44 45 watcher->AddToWindowsCloseWatcherManager(); 46 return watcher.forget(); 47 } 48 49 JSObject* CloseWatcher::WrapObject(JSContext* aCx, 50 JS::Handle<JSObject*> aGivenProto) { 51 return CloseWatcher_Binding::Wrap(aCx, this, aGivenProto); 52 } 53 54 // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close 55 bool CloseWatcher::RequestToClose(bool aRequireHistoryActionActivation) { 56 // 1. If closeWatcher is not active, then return true. 57 // 2. If the result of running closeWatcher's get enabled state is false, then 58 // return true. 59 // 3. If closeWatcher's is running cancel action is true, then return true. 60 // 4. Let window be closeWatcher's window. 61 // 5. If window's associated Document is not fully active, then return true. 62 if (!IsActive() || mIsRunningCancelAction) { 63 return true; 64 } 65 MOZ_ASSERT(GetOwnerWindow()); 66 RefPtr<WindowContext> winCtx = GetOwnerWindow()->GetWindowContext(); 67 RefPtr<CloseWatcherManager> manager = 68 GetOwnerWindow()->EnsureCloseWatcherManager(); 69 EventInit init; 70 init.mBubbles = false; 71 // 6. Let canPreventClose be true if window's close watcher manager's groups's 72 // size is less than window's close watcher manager's allowed number of 73 // groups, and window has history-action activation; otherwise false. 74 init.mCancelable = 75 !aRequireHistoryActionActivation || 76 (manager->CanGrow() && winCtx->HasValidHistoryActivation()); 77 RefPtr<Event> event = Event::Constructor(this, u"cancel"_ns, init); 78 event->SetTrusted(true); 79 // 7. Set closeWatcher's is running cancel action to true. 80 mIsRunningCancelAction = true; 81 // 8. Let shouldContinue be the result of running closeWatcher's cancel action 82 // given canPreventClose. 83 DispatchEvent(*event); 84 // 9. Set closeWatcher's is running cancel action to false. 85 mIsRunningCancelAction = false; 86 // 10. If shouldContinue is false, then: 87 if (event->DefaultPrevented()) { 88 // 10.2. Consume history-action user activation given window. 89 winCtx->ConsumeHistoryActivation(); 90 // 10.3. Return false. 91 return false; 92 } 93 // 11. Close closeWatcher. 94 Close(); 95 // 12. Return true. 96 return true; 97 } 98 99 // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-close 100 void CloseWatcher::Close() { 101 // 1. If closeWatcher is not active, then return. 102 // 2. If the result of running closeWatcher's get enabled state is false, then 103 // return true. 104 // 3. If closeWatcher's window's associated Document is not fully active, then 105 // return. 106 if (!IsActive()) { 107 return; 108 } 109 // 4. Destroy closeWatcher. 110 Destroy(); 111 EventInit init; 112 init.mBubbles = false; 113 init.mCancelable = false; 114 RefPtr<Event> event = Event::Constructor(this, u"close"_ns, init); 115 event->SetTrusted(true); 116 DispatchEvent(*event); 117 } 118 119 void CloseWatcher::AddToWindowsCloseWatcherManager() { 120 if (auto* window = GetOwnerWindow()) { 121 window->EnsureCloseWatcherManager()->Add(*this); 122 window->NotifyCloseWatcherAdded(); 123 } 124 } 125 126 void CloseWatcher::Destroy() { 127 if (auto* window = GetOwnerWindow()) { 128 window->EnsureCloseWatcherManager()->Remove(*this); 129 window->NotifyCloseWatcherRemoved(); 130 } 131 } 132 133 bool CloseWatcher::IsActive() const { 134 if (!mEnabled) { 135 return false; 136 } 137 if (auto* window = GetOwnerWindow()) { 138 return window->IsFullyActive() && 139 window->EnsureCloseWatcherManager()->Contains(*this); 140 } 141 return false; 142 } 143 144 void CloseWatcher::RunAbortAlgorithm() { Destroy(); } 145 146 } // namespace mozilla::dom