/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CloseWatcher.h" #include "mozilla/dom/CloseWatcherBinding.h" #include "mozilla/dom/CloseWatcherManager.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/EventBinding.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/WindowContext.h" #include "mozilla/RefPtr.h" #include "nsGlobalWindowInner.h" namespace mozilla::dom { NS_IMPL_ISUPPORTS_INHERITED0(CloseWatcher, DOMEventTargetHelper) already_AddRefed CloseWatcher::Constructor( const GlobalObject& aGlobal, const CloseWatcherOptions& aOptions, ErrorResult& aRv) { nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr window = global->GetAsInnerWindow(); if (!window || !window->IsFullyActive()) { aRv.ThrowInvalidStateError("The document is not fully active."); return nullptr; } RefPtr watcher = new CloseWatcher(window); AbortSignal* signal = nullptr; if (aOptions.mSignal.WasPassed()) { signal = &aOptions.mSignal.Value(); } if (signal && signal->Aborted()) { return watcher.forget(); } if (signal) { watcher->Follow(signal); } auto* manager = window->EnsureCloseWatcherManager(); manager->Add(*watcher); return watcher.forget(); } JSObject* CloseWatcher::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return CloseWatcher_Binding::Wrap(aCx, this, aGivenProto); } // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close bool CloseWatcher::RequestToClose(bool aRequireHistoryActionActivation) { // 1. If closeWatcher is not active, then return true. // 2. If the result of running closeWatcher's get enabled state is false, then // return true. // 3. If closeWatcher's is running cancel action is true, then return true. // 4. Let window be closeWatcher's window. // 5. If window's associated Document is not fully active, then return true. if (!IsActive() || mIsRunningCancelAction) { return true; } MOZ_ASSERT(GetOwnerWindow()); RefPtr winCtx = GetOwnerWindow()->GetWindowContext(); RefPtr manager = GetOwnerWindow()->EnsureCloseWatcherManager(); EventInit init; init.mBubbles = false; // 6. Let canPreventClose be true if window's close watcher manager's groups's // size is less than window's close watcher manager's allowed number of // groups, and window has history-action activation; otherwise false. init.mCancelable = !aRequireHistoryActionActivation || (manager->CanGrow() && winCtx->HasValidHistoryActivation()); RefPtr event = Event::Constructor(this, u"cancel"_ns, init); event->SetTrusted(true); // 7. Set closeWatcher's is running cancel action to true. mIsRunningCancelAction = true; // 8. Let shouldContinue be the result of running closeWatcher's cancel action // given canPreventClose. DispatchEvent(*event); // 9. Set closeWatcher's is running cancel action to false. mIsRunningCancelAction = false; // 10. If shouldContinue is false, then: if (event->DefaultPrevented()) { // 10.2. Consume history-action user activation given window. winCtx->ConsumeHistoryActivation(); // 10.3. Return false. return false; } // 11. Close closeWatcher. Close(); // 12. Return true. return true; } // https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-close void CloseWatcher::Close() { // 1. If closeWatcher is not active, then return. // 2. If the result of running closeWatcher's get enabled state is false, then // return true. // 3. If closeWatcher's window's associated Document is not fully active, then // return. if (!IsActive()) { return; } // 4. Destroy closeWatcher. Destroy(); EventInit init; init.mBubbles = false; init.mCancelable = false; RefPtr event = Event::Constructor(this, u"close"_ns, init); event->SetTrusted(true); DispatchEvent(*event); } void CloseWatcher::Destroy() { if (auto* window = GetOwnerWindow()) { window->EnsureCloseWatcherManager()->Remove(*this); } } bool CloseWatcher::IsActive() const { if (!mEnabled) { return false; } if (auto* window = GetOwnerWindow()) { return window->IsFullyActive() && window->EnsureCloseWatcherManager()->Contains(*this); } return false; } void CloseWatcher::RunAbortAlgorithm() { Destroy(); } } // namespace mozilla::dom