mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 08:42:13 +00:00
Bug 1655866: Part 4 - Handle OOP beforeunload listeners in PermitUnload(). r=nika
Differential Revision: https://phabricator.services.mozilla.com/D88317
This commit is contained in:
parent
aa7e3fbdb4
commit
0ae5bf64c5
@ -842,14 +842,21 @@ void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
|
||||
void BrowsingContext::PreOrderWalk(
|
||||
const std::function<void(BrowsingContext*)>& aCallback) {
|
||||
aCallback(this);
|
||||
for (auto& child : Children()) {
|
||||
|
||||
AutoTArray<RefPtr<BrowsingContext>, 8> children;
|
||||
children.AppendElements(Children());
|
||||
|
||||
for (auto& child : children) {
|
||||
child->PreOrderWalk(aCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void BrowsingContext::PostOrderWalk(
|
||||
const std::function<void(BrowsingContext*)>& aCallback) {
|
||||
for (auto& child : Children()) {
|
||||
AutoTArray<RefPtr<BrowsingContext>, 8> children;
|
||||
children.AppendElements(Children());
|
||||
|
||||
for (auto& child : children) {
|
||||
child->PostOrderWalk(aCallback);
|
||||
}
|
||||
|
||||
|
@ -338,3 +338,14 @@ interface nsIContentViewer : nsISupports
|
||||
[noscript, notxpcom] Encoding getHintCharset();
|
||||
[noscript, notxpcom] void setHintCharset(in Encoding aEncoding);
|
||||
};
|
||||
|
||||
%{C++
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using XPCOMPermitUnloadAction = nsIContentViewer::PermitUnloadAction;
|
||||
using PermitUnloadResult = nsIContentViewer::PermitUnloadResult;
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
%}
|
||||
|
@ -4217,6 +4217,44 @@ mozilla::ipc::IPCResult ContentChild::RecvDispatchLocationChangeEvent(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvDispatchBeforeUnloadToSubtree(
|
||||
const MaybeDiscarded<BrowsingContext>& aStartingAt,
|
||||
DispatchBeforeUnloadToSubtreeResolver&& aResolver) {
|
||||
if (aStartingAt.IsNullOrDiscarded()) {
|
||||
aResolver(nsIContentViewer::eAllowNavigation);
|
||||
} else {
|
||||
DispatchBeforeUnloadToSubtree(aStartingAt.get(), std::move(aResolver));
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
/* static */ void ContentChild::DispatchBeforeUnloadToSubtree(
|
||||
BrowsingContext* aStartingAt,
|
||||
const DispatchBeforeUnloadToSubtreeResolver& aResolver) {
|
||||
bool resolved = false;
|
||||
|
||||
aStartingAt->PreOrderWalk([&](dom::BrowsingContext* aBC) {
|
||||
if (aBC->IsInProcess()) {
|
||||
nsCOMPtr<nsIContentViewer> contentViewer;
|
||||
aBC->GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer));
|
||||
if (contentViewer &&
|
||||
contentViewer->DispatchBeforeUnload() ==
|
||||
nsIContentViewer::eRequestBlockNavigation &&
|
||||
!resolved) {
|
||||
// Send our response as soon as we find any blocker, so that we can show
|
||||
// the permit unload prompt as soon as possible, without giving
|
||||
// subsequent handlers a chance to delay it.
|
||||
aResolver(nsIContentViewer::eRequestBlockNavigation);
|
||||
resolved = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!resolved) {
|
||||
aResolver(nsIContentViewer::eAllowNavigation);
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentChild::RecvGoBack(
|
||||
const MaybeDiscarded<BrowsingContext>& aContext,
|
||||
const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction) {
|
||||
|
@ -814,9 +814,18 @@ class ContentChild final : public PContentChild,
|
||||
mozilla::ipc::IPCResult RecvDispatchLocationChangeEvent(
|
||||
const MaybeDiscarded<BrowsingContext>& aContext);
|
||||
|
||||
mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
|
||||
mozilla::ipc::IPCResult RecvDispatchBeforeUnloadToSubtree(
|
||||
const MaybeDiscarded<BrowsingContext>& aStartingAt,
|
||||
DispatchBeforeUnloadToSubtreeResolver&& aResolver);
|
||||
|
||||
public:
|
||||
static void DispatchBeforeUnloadToSubtree(
|
||||
BrowsingContext* aStartingAt,
|
||||
const DispatchBeforeUnloadToSubtreeResolver& aResolver);
|
||||
|
||||
private:
|
||||
mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override;
|
||||
#else
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDocShellLoadState.h"
|
||||
#include "nsIContentViewer.h"
|
||||
#include "mozilla/ScrollbarPreferences.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -34,6 +35,20 @@ struct ParamTraits<mozilla::ScrollbarPreference>
|
||||
mozilla::ScrollbarPreference, mozilla::ScrollbarPreference::Auto,
|
||||
mozilla::ScrollbarPreference::LAST> {};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::PermitUnloadResult>
|
||||
: public ContiguousEnumSerializerInclusive<
|
||||
mozilla::dom::PermitUnloadResult,
|
||||
mozilla::dom::PermitUnloadResult::eAllowNavigation,
|
||||
mozilla::dom::PermitUnloadResult::eRequestBlockNavigation> {};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::XPCOMPermitUnloadAction>
|
||||
: public ContiguousEnumSerializerInclusive<
|
||||
mozilla::dom::XPCOMPermitUnloadAction,
|
||||
mozilla::dom::XPCOMPermitUnloadAction::ePrompt,
|
||||
mozilla::dom::XPCOMPermitUnloadAction::eDontPromptAndUnload> {};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_docshell_message_utils_h__
|
||||
|
@ -105,6 +105,7 @@ using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
|
||||
using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
|
||||
using mozilla::dom::BrowsingContextTransaction from "mozilla/dom/BrowsingContext.h";
|
||||
using mozilla::dom::BrowsingContextInitializer from "mozilla/dom/BrowsingContext.h";
|
||||
using mozilla::dom::PermitUnloadResult from "mozilla/dom/DocShellMessageUtils.h";
|
||||
using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
|
||||
using mozilla::dom::WindowContextTransaction from "mozilla/dom/WindowContext.h";
|
||||
using base::SharedMemoryHandle from "base/shared_memory.h";
|
||||
@ -922,6 +923,12 @@ child:
|
||||
|
||||
async DispatchLocationChangeEvent(MaybeDiscardedBrowsingContext aContext);
|
||||
|
||||
// Dispatches a "beforeunload" event to each in-process content window in the
|
||||
// subtree beginning at `aStartingAt`, and returns the result as documented in
|
||||
// the `PermitUnloadResult` enum.
|
||||
async DispatchBeforeUnloadToSubtree(MaybeDiscardedBrowsingContext aStartingAt)
|
||||
returns (PermitUnloadResult result);
|
||||
|
||||
parent:
|
||||
/**
|
||||
* This is a temporary way to pass index and length through parent process.
|
||||
|
@ -20,6 +20,7 @@ using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
|
||||
using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
|
||||
using nscolor from "nsColor.h";
|
||||
using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
|
||||
using mozilla::dom::XPCOMPermitUnloadAction from "mozilla/dom/DocShellMessageUtils.h";
|
||||
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
|
||||
using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
|
||||
using refcounted class nsITransportSecurityInfo from "nsITransportSecurityInfo.h";
|
||||
@ -145,6 +146,7 @@ parent:
|
||||
TimeStamp aLoadStart,
|
||||
TimeStamp aLoadEnd);
|
||||
|
||||
|
||||
/**
|
||||
* Submit Time-to-First-Interaction telemetry correlated with whether or not
|
||||
* the document tree preloaded any resources.
|
||||
@ -157,6 +159,15 @@ parent:
|
||||
*/
|
||||
async SubmitLoadInputEventResponsePreloadTelemetry(uint32_t aMillis);
|
||||
|
||||
// Checks whether any "beforeunload" event listener in the document subtree
|
||||
// wants to block unload, and prompts the user to allow if any does (depending
|
||||
// on the action specified, using nsIContentViewer::PermitUnloadAction
|
||||
// values). The sender is responsible for checking documents in its own
|
||||
// process, and passing true for `aHasInProcessBlocker` if any exist. Windows
|
||||
// hosted outside of the caller process will be checked automatically.
|
||||
async CheckPermitUnload(bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction)
|
||||
returns (bool permitUnload);
|
||||
|
||||
async Destroy();
|
||||
};
|
||||
|
||||
|
@ -12,9 +12,11 @@
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/InProcessParent.h"
|
||||
#include "mozilla/dom/BrowserBridgeParent.h"
|
||||
#include "mozilla/dom/BrowsingContextGroup.h"
|
||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||
#include "mozilla/dom/ClientInfo.h"
|
||||
#include "mozilla/dom/ClientIPCTypes.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/BrowserHost.h"
|
||||
#include "mozilla/dom/BrowserParent.h"
|
||||
@ -39,6 +41,7 @@
|
||||
#include "nsFrameLoaderOwner.h"
|
||||
#include "nsSerializationHelper.h"
|
||||
#include "nsIBrowser.h"
|
||||
#include "nsIPromptCollection.h"
|
||||
#include "nsITransportSecurityInfo.h"
|
||||
#include "nsISharePicker.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -669,6 +672,185 @@ WindowGlobalParent::RecvSubmitLoadInputEventResponsePreloadTelemetry(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CheckPermitUnloadRequest final : public PromiseNativeHandler {
|
||||
public:
|
||||
CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
|
||||
nsIContentViewer::PermitUnloadAction aAction,
|
||||
std::function<void(bool)>&& aResolver)
|
||||
: mResolver(std::move(aResolver)),
|
||||
mWGP(aWGP),
|
||||
mAction(aAction),
|
||||
mFoundBlocker(aHasInProcessBlocker) {}
|
||||
|
||||
void Run(ContentParent* aIgnoreProcess = nullptr) {
|
||||
MOZ_ASSERT(mState == State::UNINITIALIZED);
|
||||
mState = State::WAITING;
|
||||
|
||||
RefPtr<CheckPermitUnloadRequest> self(this);
|
||||
|
||||
AutoTArray<ContentParent*, 8> seen;
|
||||
if (aIgnoreProcess) {
|
||||
seen.AppendElement(aIgnoreProcess);
|
||||
}
|
||||
|
||||
BrowsingContext* bc = mWGP->GetBrowsingContext();
|
||||
bc->PreOrderWalk([&](dom::BrowsingContext* aBC) {
|
||||
if (WindowGlobalParent* wgp =
|
||||
aBC->Canonical()->GetCurrentWindowGlobal()) {
|
||||
ContentParent* cp = wgp->GetContentParent();
|
||||
if (wgp->HasBeforeUnload() && !seen.ContainsSorted(cp)) {
|
||||
seen.InsertElementSorted(cp);
|
||||
mPendingRequests++;
|
||||
auto resolve = [self](bool blockNavigation) {
|
||||
if (blockNavigation) {
|
||||
self->mFoundBlocker = true;
|
||||
}
|
||||
self->ResolveRequest();
|
||||
};
|
||||
if (cp) {
|
||||
cp->SendDispatchBeforeUnloadToSubtree(
|
||||
bc, resolve, [self](auto) { self->ResolveRequest(); });
|
||||
} else {
|
||||
ContentChild::DispatchBeforeUnloadToSubtree(bc, resolve);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CheckDoneWaiting();
|
||||
}
|
||||
|
||||
void ResolveRequest() {
|
||||
mPendingRequests--;
|
||||
CheckDoneWaiting();
|
||||
}
|
||||
|
||||
void CheckDoneWaiting() {
|
||||
// If we've found a blocker, we prompt immediately without waiting for
|
||||
// further responses. The user's response applies to the entire navigation
|
||||
// attempt, regardless of how many "beforeunload" listeners we call.
|
||||
if (mState != State::WAITING || (mPendingRequests && !mFoundBlocker)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mState = State::PROMPTING;
|
||||
|
||||
if (!mFoundBlocker) {
|
||||
SendReply(true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = mAction;
|
||||
if (StaticPrefs::dom_disable_beforeunload()) {
|
||||
action = nsIContentViewer::eDontPromptAndUnload;
|
||||
}
|
||||
if (action != nsIContentViewer::ePrompt) {
|
||||
SendReply(action == nsIContentViewer::eDontPromptAndUnload);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle any failure in prompting by aborting the navigation. See comment
|
||||
// in nsContentViewer::PermitUnload for reasoning.
|
||||
auto cleanup = MakeScopeExit([&]() { SendReply(false); });
|
||||
|
||||
if (nsCOMPtr<nsIPromptCollection> prompt =
|
||||
do_GetService("@mozilla.org/embedcomp/prompt-collection;1")) {
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_COUNT, 1);
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
prompt->AsyncBeforeUnloadCheck(mWGP->GetBrowsingContext(),
|
||||
getter_AddRefs(promise));
|
||||
|
||||
if (!promise) {
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
promise->AppendNativeHandler(this);
|
||||
cleanup.release();
|
||||
}
|
||||
}
|
||||
|
||||
void SendReply(bool aAllow) {
|
||||
MOZ_ASSERT(mState != State::REPLIED);
|
||||
mResolver(aAllow);
|
||||
mState = State::REPLIED;
|
||||
}
|
||||
|
||||
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
|
||||
MOZ_ASSERT(mState == State::PROMPTING);
|
||||
|
||||
bool allow = JS::ToBoolean(aValue);
|
||||
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION, (allow ? 1 : 0));
|
||||
|
||||
SendReply(allow);
|
||||
}
|
||||
|
||||
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
|
||||
MOZ_ASSERT(mState == State::PROMPTING);
|
||||
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION, 2);
|
||||
|
||||
SendReply(false);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
private:
|
||||
~CheckPermitUnloadRequest() {
|
||||
// We may get here without having sent a reply if the promise we're waiting
|
||||
// on is destroyed without being resolved or rejected.
|
||||
if (mState != State::REPLIED) {
|
||||
SendReply(false);
|
||||
}
|
||||
}
|
||||
|
||||
enum class State : uint8_t {
|
||||
UNINITIALIZED,
|
||||
WAITING,
|
||||
PROMPTING,
|
||||
REPLIED,
|
||||
};
|
||||
|
||||
std::function<void(bool)> mResolver;
|
||||
|
||||
RefPtr<WindowGlobalParent> mWGP;
|
||||
|
||||
uint32_t mPendingRequests = 0;
|
||||
|
||||
nsIContentViewer::PermitUnloadAction mAction;
|
||||
|
||||
State mState = State::UNINITIALIZED;
|
||||
|
||||
bool mFoundBlocker = false;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS0(CheckPermitUnloadRequest)
|
||||
|
||||
} // namespace
|
||||
|
||||
mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
|
||||
bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
|
||||
CheckPermitUnloadResolver&& aResolver) {
|
||||
if (!IsCurrentGlobal()) {
|
||||
aResolver(false);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
auto request = MakeRefPtr<CheckPermitUnloadRequest>(
|
||||
this, aHasInProcessBlocker, aAction, std::move(aResolver));
|
||||
request->Run(/* aIgnoreProcess */ GetContentParent());
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
|
||||
const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
|
||||
mozilla::ErrorResult& aRv) {
|
||||
|
@ -254,6 +254,10 @@ class WindowGlobalParent final : public WindowContext,
|
||||
mozilla::ipc::IPCResult RecvSubmitLoadInputEventResponsePreloadTelemetry(
|
||||
uint32_t aMillis);
|
||||
|
||||
mozilla::ipc::IPCResult RecvCheckPermitUnload(
|
||||
bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
|
||||
CheckPermitUnloadResolver&& aResolver);
|
||||
|
||||
private:
|
||||
WindowGlobalParent(CanonicalBrowsingContext* aBrowsingContext,
|
||||
uint64_t aInnerWindowId, uint64_t aOuterWindowId,
|
||||
|
@ -1201,7 +1201,6 @@ nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
|
||||
aAction = eDontPromptAndUnload;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
*aPermitUnload = true;
|
||||
|
||||
RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
|
||||
@ -1216,6 +1215,7 @@ nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
|
||||
IgnoreOpensDuringUnload ignoreOpens(mDocument);
|
||||
|
||||
bool foundBlocker = false;
|
||||
bool foundOOPListener = false;
|
||||
bc->PreOrderWalk([&](BrowsingContext* aBC) {
|
||||
if (aBC->IsInProcess()) {
|
||||
nsCOMPtr<nsIContentViewer> contentViewer;
|
||||
@ -1224,30 +1224,45 @@ nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
|
||||
contentViewer->DispatchBeforeUnload() == eRequestBlockNavigation) {
|
||||
foundBlocker = true;
|
||||
}
|
||||
} else {
|
||||
WindowContext* wc = aBC->GetCurrentWindowContext();
|
||||
if (wc && wc->HasBeforeUnload()) {
|
||||
foundOOPListener = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!foundOOPListener) {
|
||||
if (!foundBlocker) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (aAction != ePrompt) {
|
||||
*aPermitUnload = aAction == eDontPromptAndUnload;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// NB: we nullcheck mDocument because it might now be dead as a result of
|
||||
// the event being dispatched.
|
||||
if (!mDocument) {
|
||||
RefPtr<WindowGlobalChild> wgc(mDocument ? mDocument->GetWindowGlobalChild()
|
||||
: nullptr);
|
||||
if (!wgc) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (foundBlocker) {
|
||||
if (aAction == eDontPromptAndUnload) {
|
||||
// Ask the user if it's ok to unload the current page
|
||||
nsAutoSyncOperation sync(mDocument);
|
||||
AutoSuppressEventHandlingAndSuspend seh(bc->Group());
|
||||
|
||||
nsCOMPtr<nsIPromptCollection> prompt =
|
||||
do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
|
||||
|
||||
if (prompt) {
|
||||
nsAutoSyncOperation sync(mDocument);
|
||||
mInPermitUnloadPrompt = true;
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_COUNT, 1);
|
||||
rv = prompt->BeforeUnloadCheck(bc, aPermitUnload);
|
||||
mInPermitUnloadPrompt = false;
|
||||
mInPermitUnloadPrompt = true;
|
||||
|
||||
bool done = false;
|
||||
wgc->SendCheckPermitUnload(
|
||||
foundBlocker, aAction,
|
||||
[&](bool aPermit) {
|
||||
done = true;
|
||||
*aPermitUnload = aPermit;
|
||||
},
|
||||
[&](auto) {
|
||||
// If the prompt aborted, we tell our consumer that it is not allowed
|
||||
// to unload the page. One reason that prompts abort is that the user
|
||||
// performed some action that caused the page to unload while our prompt
|
||||
@ -1256,22 +1271,13 @@ nsDocumentViewer::PermitUnload(PermitUnloadAction aAction,
|
||||
//
|
||||
// XXX: Are there other cases where prompts can abort? Is it ok to
|
||||
// prevent unloading the page in those cases?
|
||||
if (NS_FAILED(rv)) {
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION, 2);
|
||||
*aPermitUnload = false;
|
||||
return NS_OK;
|
||||
}
|
||||
done = true;
|
||||
*aPermitUnload = false;
|
||||
});
|
||||
|
||||
mozilla::Telemetry::Accumulate(
|
||||
mozilla::Telemetry::ONBEFOREUNLOAD_PROMPT_ACTION,
|
||||
(*aPermitUnload ? 1 : 0));
|
||||
}
|
||||
} else if (aAction == eDontPromptAndDontUnload) {
|
||||
*aPermitUnload = false;
|
||||
}
|
||||
}
|
||||
SpinEventLoopUntil([&]() { return done; });
|
||||
|
||||
mInPermitUnloadPrompt = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user