Bug 1336763 - Add a hasBeforeUnload attribute to nsITabParent. r=Ehsan

This will return true if any of the frames loaded in the associated
TabChild have set at least one onbeforeunload event handler. If those
handlers are all removed, or all of the documents with onbeforeunload
event handlers are unloaded, this becomes false again.

Note that subframes that are sandboxed without the allow-modals
permission will not affect the hasBeforeUnload attribute, since
those iframes should never cause the beforeunload confirmation
dialog to display.

MozReview-Commit-ID: 8b0gBYWwMDn

--HG--
extra : rebase_source : 69f3b692d6e73f6277e6982aad02bcd1ebdd8acf
This commit is contained in:
Mike Conley 2017-04-13 17:54:07 -04:00
Родитель 750b3f5764
Коммит cf1d141d21
9 изменённых файлов: 102 добавлений и 1 удалений

Просмотреть файл

@ -1570,7 +1570,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mIsValidatingTabGroup(false),
#endif
mCanSkipCCGeneration(0),
mAutoActivateVRDisplayID(0)
mAutoActivateVRDisplayID(0),
mBeforeUnloadListenerCount(0)
{
AssertIsOnMainThread();
@ -1607,6 +1608,13 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
MOZ_ASSERT(IsFrozen());
}
if (XRE_IsContentProcess()) {
nsCOMPtr<nsIDocShell> docShell = GetDocShell();
if (docShell) {
mTabChild = docShell->GetTabChild();
}
}
// We could have failed the first time through trying
// to create the entropy collector, so we should
// try to get one until we succeed.
@ -2122,6 +2130,12 @@ nsGlobalWindow::FreeInnerObjects()
DisableVRUpdates();
mHasVREvents = false;
mVRDisplays.Clear();
if (mTabChild) {
while (mBeforeUnloadListenerCount-- > 0) {
mTabChild->BeforeUnloadRemoved();
}
}
}
//*****************************************************************************
@ -2261,6 +2275,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
@ -2344,6 +2359,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
@ -13411,6 +13427,15 @@ nsGlobalWindow::EventListenerAdded(nsIAtom* aType)
NotifyVREventListenerAdded();
}
if (aType == nsGkAtoms::onbeforeunload &&
mTabChild &&
(!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS))) {
MOZ_ASSERT(IsInnerWindow());
mBeforeUnloadListenerCount++;
MOZ_ASSERT(mBeforeUnloadListenerCount > 0);
mTabChild->BeforeUnloadAdded();
}
// We need to initialize localStorage in order to receive notifications.
if (aType == nsGkAtoms::onstorage) {
ErrorResult rv;
@ -13419,6 +13444,19 @@ nsGlobalWindow::EventListenerAdded(nsIAtom* aType)
}
}
void
nsGlobalWindow::EventListenerRemoved(nsIAtom* aType)
{
if (aType == nsGkAtoms::onbeforeunload &&
mTabChild &&
(!mDoc || !(mDoc->GetSandboxFlags() & SANDBOXED_MODALS))) {
MOZ_ASSERT(IsInnerWindow());
mBeforeUnloadListenerCount--;
MOZ_ASSERT(mBeforeUnloadListenerCount >= 0);
mTabChild->BeforeUnloadRemoved();
}
}
void
nsGlobalWindow::NotifyVREventListenerAdded()
{

Просмотреть файл

@ -87,6 +87,7 @@ class nsIControllers;
class nsIJSID;
class nsIScriptContext;
class nsIScriptTimeoutHandler;
class nsITabChild;
class nsITimeoutHandler;
class nsIWebBrowserChrome;
class mozIDOMWindowProxy;
@ -459,6 +460,8 @@ public:
using EventTarget::EventListenerAdded;
virtual void EventListenerAdded(nsIAtom* aType) override;
using EventTarget::EventListenerRemoved;
virtual void EventListenerRemoved(nsIAtom* aType) override;
// nsIInterfaceRequestor
NS_DECL_NSIINTERFACEREQUESTOR
@ -1952,6 +1955,8 @@ protected:
// These member variables are used on both inner and the outer windows.
nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
// mTabChild is only ever populated in the content process.
nsCOMPtr<nsITabChild> mTabChild;
uint32_t mSuspendDepth;
uint32_t mFreezeDepth;
@ -2040,6 +2045,7 @@ protected:
// after loading. The value is the ID of the VRDisplay that content should
// begin presentation on.
uint32_t mAutoActivateVRDisplayID; // Outer windows only
int64_t mBeforeUnloadListenerCount; // Inner windows only
#ifdef ENABLE_INTL_API
RefPtr<mozilla::dom::IntlUtils> mIntlUtils;

Просмотреть файл

@ -34,5 +34,8 @@ interface nsITabChild : nsISupports
[array, size_is(linksCount)] in nsIDroppedLinkItem links);
readonly attribute uint64_t tabId;
[noscript, notxpcom] void beforeUnloadAdded();
[noscript, notxpcom] void beforeUnloadRemoved();
};

Просмотреть файл

@ -69,4 +69,10 @@ interface nsITabParent : nsISupports
* permissions required to load a document with the given principal.
*/
void transmitPermissionsForPrincipal(in nsIPrincipal aPrincipal);
/**
* True if any of the frames loaded in the TabChild have registered
* an onbeforeunload event handler.
*/
readonly attribute boolean hasBeforeUnload;
};

Просмотреть файл

@ -555,6 +555,8 @@ parent:
async AccessKeyNotHandled(WidgetKeyboardEvent event);
async SetHasBeforeUnload(bool aHasBeforeUnload);
child:
async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);

Просмотреть файл

@ -379,6 +379,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mChromeFlags(aChromeFlags)
, mActiveSuppressDisplayport(0)
, mLayersId(0)
, mBeforeUnloadListeners(0)
, mLayersConnected(true)
, mDidFakeShow(false)
, mNotified(false)
@ -3315,6 +3316,28 @@ TabChild::ForcePaint(uint64_t aLayerObserverEpoch)
RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
}
void
TabChild::BeforeUnloadAdded()
{
if (mBeforeUnloadListeners == 0) {
SendSetHasBeforeUnload(true);
}
mBeforeUnloadListeners++;
MOZ_ASSERT(mBeforeUnloadListeners >= 0);
}
void
TabChild::BeforeUnloadRemoved()
{
mBeforeUnloadListeners--;
MOZ_ASSERT(mBeforeUnloadListeners >= 0);
if (mBeforeUnloadListeners == 0) {
SendSetHasBeforeUnload(false);
}
}
already_AddRefed<nsISHistory>
TabChild::GetRelatedSHistory()
{

Просмотреть файл

@ -780,6 +780,7 @@ private:
uint32_t mChromeFlags;
int32_t mActiveSuppressDisplayport;
uint64_t mLayersId;
int64_t mBeforeUnloadListeners;
CSSRect mUnscaledOuterRect;
nscolor mLastBackgroundColor;
bool mLayersConnected;

Просмотреть файл

@ -168,6 +168,7 @@ TabParent::TabParent(nsIContentParent* aManager,
, mLayerTreeEpoch(0)
, mPreserveLayers(false)
, mHasPresented(false)
, mHasBeforeUnload(false)
{
MOZ_ASSERT(aManager);
}
@ -2025,6 +2026,13 @@ TabParent::RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent)
return IPC_OK();
}
mozilla::ipc::IPCResult
TabParent::RecvSetHasBeforeUnload(const bool& aHasBeforeUnload)
{
mHasBeforeUnload = aHasBeforeUnload;
return IPC_OK();
}
bool
TabParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent)
{
@ -2797,6 +2805,13 @@ TabParent::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal)
->TransmitPermissionsForPrincipal(aPrincipal);
}
NS_IMETHODIMP
TabParent::GetHasBeforeUnload(bool* aResult)
{
*aResult = mHasBeforeUnload;
return NS_OK;
}
class LayerTreeUpdateRunnable final
: public mozilla::Runnable
{

Просмотреть файл

@ -170,6 +170,9 @@ public:
virtual mozilla::ipc::IPCResult
RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent) override;
virtual mozilla::ipc::IPCResult
RecvSetHasBeforeUnload(const bool& aHasBeforeUnload) override;
virtual mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
PRenderFrameParent* aRenderFrame,
const nsString& aURL,
@ -762,6 +765,10 @@ private:
// at least once.
bool mHasPresented;
// True if at least one window hosted in the TabChild has added a
// beforeunload event listener.
bool mHasBeforeUnload;
public:
static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
};