mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process (r=Gijs)
This commit is contained in:
parent
e985043b20
commit
a1d806a296
@ -5009,6 +5009,10 @@ nsBrowserAccess.prototype = {
|
||||
isTabContentWindow: function (aWindow) {
|
||||
return gBrowser.browsers.some(browser => browser.contentWindow == aWindow);
|
||||
},
|
||||
|
||||
canClose() {
|
||||
return CanCloseWindow();
|
||||
},
|
||||
}
|
||||
|
||||
function getTogglableToolbars() {
|
||||
@ -6565,6 +6569,26 @@ var IndexedDBPromptHelper = {
|
||||
}
|
||||
};
|
||||
|
||||
function CanCloseWindow()
|
||||
{
|
||||
// Avoid redundant calls to canClose from showing multiple
|
||||
// PermitUnload dialogs.
|
||||
if (window.skipNextCanClose) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let browser of gBrowser.browsers) {
|
||||
let {permitUnload, timedOut} = browser.permitUnload();
|
||||
if (timedOut) {
|
||||
return true;
|
||||
}
|
||||
if (!permitUnload) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function WindowIsClosing()
|
||||
{
|
||||
if (TabView.isVisible()) {
|
||||
@ -6575,27 +6599,19 @@ function WindowIsClosing()
|
||||
if (!closeWindow(false, warnAboutClosingWindow))
|
||||
return false;
|
||||
|
||||
// Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
|
||||
if (gMultiProcessBrowser)
|
||||
// In theory we should exit here and the Window's internal Close
|
||||
// method should trigger canClose on nsBrowserAccess. However, by
|
||||
// that point it's too late to be able to show a prompt for
|
||||
// PermitUnload. So we do it here, when we still can.
|
||||
if (CanCloseWindow()) {
|
||||
// This flag ensures that the later canClose call does nothing.
|
||||
// It's only needed to make tests pass, since they detect the
|
||||
// prompt even when it's not actually shown.
|
||||
window.skipNextCanClose = true;
|
||||
return true;
|
||||
|
||||
for (let browser of gBrowser.browsers) {
|
||||
let ds = browser.docShell;
|
||||
// Passing true to permitUnload indicates we plan on closing the window.
|
||||
// This means that once unload is permitted, all further calls to
|
||||
// permitUnload will be ignored. This avoids getting multiple prompts
|
||||
// to unload the page.
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
|
||||
// ... however, if the user aborts closing, we need to undo that,
|
||||
// to ensure they get prompted again when we next try to close the window.
|
||||
// We do this on the window's toplevel docshell instead of on the tab, so
|
||||
// that all tabs we iterated before will get this reset.
|
||||
window.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,6 +131,11 @@ chatBrowserAccess.prototype = {
|
||||
isTabContentWindow: function (aWindow) {
|
||||
return this.contentWindow == aWindow;
|
||||
},
|
||||
|
||||
canClose() {
|
||||
let {BrowserUtils} = Cu.import("resource://gre/modules/BrowserUtils.jsm", {});
|
||||
return BrowserUtils.canCloseWindow(window);
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
|
||||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
/* 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/. */
|
||||
@ -567,28 +567,17 @@ Sanitizer.prototype = {
|
||||
openWindows: {
|
||||
privateStateForNewWindow: "non-private",
|
||||
_canCloseWindow: function(aWindow) {
|
||||
// Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
|
||||
if (!aWindow.gMultiProcessBrowser) {
|
||||
// Cargo-culted out of browser.js' WindowIsClosing because we don't care
|
||||
// about TabView or the regular 'warn me before closing windows with N tabs'
|
||||
// stuff here, and more importantly, we want to set aCallerClosesWindow to true
|
||||
// when calling into permitUnload:
|
||||
for (let browser of aWindow.gBrowser.browsers) {
|
||||
let ds = browser.docShell;
|
||||
// 'true' here means we will be closing the window soon, so please don't dispatch
|
||||
// another onbeforeunload event when we do so. If unload is *not* permitted somewhere,
|
||||
// we will reset the flag that this triggers everywhere so that we don't interfere
|
||||
// with the browser after all:
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (aWindow.CanCloseWindow()) {
|
||||
// We already showed PermitUnload for the window, so let's
|
||||
// make sure we don't do it again when we actually close the
|
||||
// window.
|
||||
aWindow.skipNextCanClose = true;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
_resetAllWindowClosures: function(aWindowList) {
|
||||
for (let win of aWindowList) {
|
||||
win.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
|
||||
win.skipNextCanClose = false;
|
||||
}
|
||||
},
|
||||
clear: Task.async(function*() {
|
||||
|
@ -1156,25 +1156,29 @@
|
||||
this._tabAttrModified(this.mCurrentTab, ["selected"]);
|
||||
|
||||
if (oldBrowser != newBrowser &&
|
||||
oldBrowser.docShell &&
|
||||
oldBrowser.docShell.contentViewer.inPermitUnload) {
|
||||
// Since the user is switching away from a tab that has
|
||||
// a beforeunload prompt active, we remove the prompt.
|
||||
// This prevents confusing user flows like the following:
|
||||
// 1. User attempts to close Firefox
|
||||
// 2. User switches tabs (ingoring a beforeunload prompt)
|
||||
// 3. User returns to tab, presses "Leave page"
|
||||
let promptBox = this.getTabModalPromptBox(oldBrowser);
|
||||
let prompts = promptBox.listPrompts();
|
||||
// There might not be any prompts here if the tab was closed
|
||||
// while in an onbeforeunload prompt, which will have
|
||||
// destroyed aforementioned prompt already, so check there's
|
||||
// something to remove, first:
|
||||
if (prompts.length) {
|
||||
// NB: This code assumes that the beforeunload prompt
|
||||
// is the top-most prompt on the tab.
|
||||
prompts[prompts.length - 1].abortPrompt();
|
||||
}
|
||||
oldBrowser.getInPermitUnload) {
|
||||
oldBrowser.getInPermitUnload(inPermitUnload => {
|
||||
if (!inPermitUnload) {
|
||||
return;
|
||||
}
|
||||
// Since the user is switching away from a tab that has
|
||||
// a beforeunload prompt active, we remove the prompt.
|
||||
// This prevents confusing user flows like the following:
|
||||
// 1. User attempts to close Firefox
|
||||
// 2. User switches tabs (ingoring a beforeunload prompt)
|
||||
// 3. User returns to tab, presses "Leave page"
|
||||
let promptBox = this.getTabModalPromptBox(oldBrowser);
|
||||
let prompts = promptBox.listPrompts();
|
||||
// There might not be any prompts here if the tab was closed
|
||||
// while in an onbeforeunload prompt, which will have
|
||||
// destroyed aforementioned prompt already, so check there's
|
||||
// something to remove, first:
|
||||
if (prompts.length) {
|
||||
// NB: This code assumes that the beforeunload prompt
|
||||
// is the top-most prompt on the tab.
|
||||
prompts[prompts.length - 1].abortPrompt();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
|
||||
@ -2103,6 +2107,7 @@
|
||||
if (aParams) {
|
||||
var animate = aParams.animate;
|
||||
var byMouse = aParams.byMouse;
|
||||
var skipPermitUnload = aParams.skipPermitUnload;
|
||||
}
|
||||
|
||||
// Handle requests for synchronously removing an already
|
||||
@ -2115,7 +2120,7 @@
|
||||
|
||||
var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
|
||||
|
||||
if (!this._beginRemoveTab(aTab, false, null, true))
|
||||
if (!this._beginRemoveTab(aTab, false, null, true, skipPermitUnload))
|
||||
return;
|
||||
|
||||
if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
|
||||
@ -2163,6 +2168,7 @@
|
||||
<parameter name="aTabWillBeMoved"/>
|
||||
<parameter name="aCloseWindowWithLastTab"/>
|
||||
<parameter name="aCloseWindowFastpath"/>
|
||||
<parameter name="aSkipPermitUnload"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (aTab.closing ||
|
||||
@ -2193,23 +2199,20 @@
|
||||
newTab = true;
|
||||
}
|
||||
|
||||
if (!aTab._pendingPermitUnload && !aTabWillBeMoved) {
|
||||
let ds = browser.docShell;
|
||||
if (ds && ds.contentViewer) {
|
||||
// We need to block while calling permitUnload() because it
|
||||
// processes the event queue and may lead to another removeTab()
|
||||
// call before permitUnload() returns.
|
||||
aTab._pendingPermitUnload = true;
|
||||
let permitUnload = ds.contentViewer.permitUnload();
|
||||
delete aTab._pendingPermitUnload;
|
||||
// If we were closed during onbeforeunload, we return false now
|
||||
// so we don't (try to) close the same tab again. Of course, we
|
||||
// also stop if the unload was cancelled by the user:
|
||||
if (aTab.closing || !permitUnload) {
|
||||
// NB: deliberately keep the _closedDuringPermitUnload set to
|
||||
// true so we keep exiting early in case of multiple calls.
|
||||
return false;
|
||||
}
|
||||
if (!aTab._pendingPermitUnload && !aTabWillBeMoved && !aSkipPermitUnload) {
|
||||
// We need to block while calling permitUnload() because it
|
||||
// processes the event queue and may lead to another removeTab()
|
||||
// call before permitUnload() returns.
|
||||
aTab._pendingPermitUnload = true;
|
||||
let {permitUnload} = browser.permitUnload();
|
||||
delete aTab._pendingPermitUnload;
|
||||
// If we were closed during onbeforeunload, we return false now
|
||||
// so we don't (try to) close the same tab again. Of course, we
|
||||
// also stop if the unload was cancelled by the user:
|
||||
if (aTab.closing || !permitUnload) {
|
||||
// NB: deliberately keep the _closedDuringPermitUnload set to
|
||||
// true so we keep exiting early in case of multiple calls.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4027,13 +4030,19 @@
|
||||
}
|
||||
case "DOMWindowClose": {
|
||||
if (this.tabs.length == 1) {
|
||||
// We already did PermitUnload in the content process
|
||||
// for this tab (the only one in the window). So we don't
|
||||
// need to do it again for any tabs.
|
||||
window.skipNextCanClose = true;
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
let tab = this.getTabForBrowser(browser);
|
||||
if (tab) {
|
||||
this.removeTab(tab);
|
||||
// Skip running PermitUnload since it already happened in
|
||||
// the content process.
|
||||
this.removeTab(tab, {skipPermitUnload: true});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -4317,12 +4326,18 @@
|
||||
if (!event.isTrusted)
|
||||
return;
|
||||
|
||||
if (this.tabs.length == 1)
|
||||
if (this.tabs.length == 1) {
|
||||
// We already did PermitUnload in nsGlobalWindow::Close
|
||||
// for this tab. There are no other tabs we need to do
|
||||
// PermitUnload for.
|
||||
window.skipNextCanClose = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = this._getTabForContentWindow(event.target);
|
||||
if (tab) {
|
||||
this.removeTab(tab);
|
||||
// Skip running PermitUnload since it already happened.
|
||||
this.removeTab(tab, {skipPermitUnload: true});
|
||||
event.preventDefault();
|
||||
}
|
||||
]]>
|
||||
|
@ -149,7 +149,6 @@ skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir
|
||||
[browser_backButtonFitts.js]
|
||||
skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
|
||||
[browser_beforeunload_duplicate_dialogs.js]
|
||||
skip-if = e10s # bug 967873 means permitUnload doesn't work in e10s mode
|
||||
[browser_blob-channelname.js]
|
||||
[browser_bookmark_titles.js]
|
||||
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
|
||||
|
@ -7805,7 +7805,7 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
|
||||
mTiming->NotifyBeforeUnload();
|
||||
|
||||
bool okToUnload;
|
||||
rv = mContentViewer->PermitUnload(false, &okToUnload);
|
||||
rv = mContentViewer->PermitUnload(&okToUnload);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !okToUnload) {
|
||||
// The user chose not to unload the page, interrupt the load.
|
||||
@ -10143,7 +10143,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
||||
// protocol handler deals with this for javascript: URLs.
|
||||
if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
|
||||
bool okToUnload;
|
||||
rv = mContentViewer->PermitUnload(false, &okToUnload);
|
||||
rv = mContentViewer->PermitUnload(&okToUnload);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !okToUnload) {
|
||||
// The user chose not to unload the page, interrupt the
|
||||
|
@ -31,7 +31,7 @@ class nsDOMNavigationTiming;
|
||||
[ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
|
||||
[ref] native nsIContentViewerTArray(nsTArray<nsCOMPtr<nsIContentViewer> >);
|
||||
|
||||
[scriptable, builtinclass, uuid(fbd04c99-e149-473f-8a68-44f53d82f98b)]
|
||||
[scriptable, builtinclass, uuid(91b6c1f3-fc5f-43a9-88f4-9286bd19387f)]
|
||||
interface nsIContentViewer : nsISupports
|
||||
{
|
||||
[noscript] void init(in nsIWidgetPtr aParentWidget,
|
||||
@ -45,12 +45,8 @@ interface nsIContentViewer : nsISupports
|
||||
/**
|
||||
* Checks if the document wants to prevent unloading by firing beforeunload on
|
||||
* the document, and if it does, prompts the user. The result is returned.
|
||||
*
|
||||
* @param aCallerClosesWindow indicates that the current caller will close the
|
||||
* window. If the method returns true, all subsequent calls will be
|
||||
* ignored.
|
||||
*/
|
||||
boolean permitUnload([optional] in boolean aCallerClosesWindow);
|
||||
boolean permitUnload();
|
||||
|
||||
/**
|
||||
* Exposes whether we're blocked in a call to permitUnload.
|
||||
@ -62,8 +58,7 @@ interface nsIContentViewer : nsISupports
|
||||
* track of whether the user has responded to a prompt.
|
||||
* Used internally by the scriptable version to ensure we only prompt once.
|
||||
*/
|
||||
[noscript,nostdcall] boolean permitUnloadInternal(in boolean aCallerClosesWindow,
|
||||
inout boolean aShouldPrompt);
|
||||
[noscript,nostdcall] boolean permitUnloadInternal(inout boolean aShouldPrompt);
|
||||
|
||||
/**
|
||||
* Exposes whether we're in the process of firing the beforeunload event.
|
||||
@ -71,16 +66,6 @@ interface nsIContentViewer : nsISupports
|
||||
*/
|
||||
readonly attribute boolean beforeUnloadFiring;
|
||||
|
||||
/**
|
||||
* Works in tandem with permitUnload, if the caller decides not to close the
|
||||
* window it indicated it will, it is the caller's responsibility to reset
|
||||
* that with this method.
|
||||
*
|
||||
* @Note this method is only meant to be called on documents for which the
|
||||
* caller has indicated that it will close the window. If that is not the case
|
||||
* the behavior of this method is undefined.
|
||||
*/
|
||||
void resetCloseWindow();
|
||||
void pageHide(in boolean isUnload);
|
||||
|
||||
/**
|
||||
|
@ -83,7 +83,6 @@ skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
|
||||
[browser_loadURI.js]
|
||||
[browser_multiple_pushState.js]
|
||||
[browser_onbeforeunload_navigation.js]
|
||||
skip-if = e10s
|
||||
[browser_search_notification.js]
|
||||
[browser_timelineMarkers-01.js]
|
||||
[browser_timelineMarkers-02.js]
|
||||
|
@ -7998,6 +7998,17 @@ nsGlobalWindow::CanClose()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (mIsChrome) {
|
||||
nsCOMPtr<nsIBrowserDOMWindow> bwin;
|
||||
nsIDOMChromeWindow* chromeWin = static_cast<nsGlobalChromeWindow*>(this);
|
||||
chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
|
||||
|
||||
bool canClose = true;
|
||||
if (bwin && NS_SUCCEEDED(bwin->CanClose(&canClose))) {
|
||||
return canClose;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDocShell) {
|
||||
return true;
|
||||
}
|
||||
@ -8011,7 +8022,7 @@ nsGlobalWindow::CanClose()
|
||||
mDocShell->GetContentViewer(getter_AddRefs(cv));
|
||||
if (cv) {
|
||||
bool canClose;
|
||||
nsresult rv = cv->PermitUnload(false, &canClose);
|
||||
nsresult rv = cv->PermitUnload(&canClose);
|
||||
if (NS_SUCCEEDED(rv) && !canClose)
|
||||
return false;
|
||||
|
||||
|
@ -1522,7 +1522,7 @@ nsHTMLDocument::Open(JSContext* cx,
|
||||
|
||||
if (cv) {
|
||||
bool okToUnload;
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) && !okToUnload) {
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
|
||||
// We don't want to unload, so stop here, but don't throw an
|
||||
// exception.
|
||||
nsCOMPtr<nsIDocument> ret = this;
|
||||
|
@ -17,7 +17,7 @@ interface nsIOpenURIInFrameParams : nsISupports
|
||||
attribute boolean isPrivate;
|
||||
};
|
||||
|
||||
[scriptable, uuid(99f5a347-722c-4337-bd38-f14ec94801b3)]
|
||||
[scriptable, uuid(31da1ce2-aec4-4c26-ac66-d622935c3bf4)]
|
||||
|
||||
/**
|
||||
* The C++ source has access to the browser script source through
|
||||
@ -99,6 +99,14 @@ interface nsIBrowserDOMWindow : nsISupports
|
||||
* @return whether the window is the main content window for any
|
||||
* currently open tab in this toplevel browser window.
|
||||
*/
|
||||
boolean isTabContentWindow(in nsIDOMWindow aWindow);
|
||||
boolean isTabContentWindow(in nsIDOMWindow aWindow);
|
||||
|
||||
/**
|
||||
* This function is responsible for calling
|
||||
* nsIContentViewer::PermitUnload on each frame in the window. It
|
||||
* returns true if closing the window is allowed. See canClose() in
|
||||
* BrowserUtils.jsm for a simple implementation of this method.
|
||||
*/
|
||||
boolean canClose();
|
||||
};
|
||||
|
||||
|
@ -749,7 +749,7 @@ nsJSChannel::EvaluateScript()
|
||||
if (cv) {
|
||||
bool okToUnload;
|
||||
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(false, &okToUnload)) &&
|
||||
if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) &&
|
||||
!okToUnload) {
|
||||
// The user didn't want to unload the current
|
||||
// page, translate this into an undefined
|
||||
|
@ -404,7 +404,6 @@ protected:
|
||||
nsCString mForceCharacterSet;
|
||||
|
||||
bool mIsPageMode;
|
||||
bool mCallerIsClosingWindow;
|
||||
bool mInitializedForPrintPreview;
|
||||
bool mHidden;
|
||||
};
|
||||
@ -455,7 +454,6 @@ void nsDocumentViewer::PrepareToStartLoad()
|
||||
mLoaded = false;
|
||||
mAttachedToParent = false;
|
||||
mDeferredWindowClose = false;
|
||||
mCallerIsClosingWindow = false;
|
||||
|
||||
#ifdef NS_PRINTING
|
||||
mPrintIsPending = false;
|
||||
@ -1051,18 +1049,15 @@ nsDocumentViewer::LoadComplete(nsresult aStatus)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocumentViewer::PermitUnload(bool aCallerClosesWindow,
|
||||
bool *aPermitUnload)
|
||||
nsDocumentViewer::PermitUnload(bool *aPermitUnload)
|
||||
{
|
||||
bool shouldPrompt = true;
|
||||
return PermitUnloadInternal(aCallerClosesWindow, &shouldPrompt,
|
||||
aPermitUnload);
|
||||
return PermitUnloadInternal(&shouldPrompt, aPermitUnload);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
|
||||
bool *aShouldPrompt,
|
||||
nsDocumentViewer::PermitUnloadInternal(bool *aShouldPrompt,
|
||||
bool *aPermitUnload)
|
||||
{
|
||||
AutoDontWarnAboutSyncXHR disableSyncXHRWarning;
|
||||
@ -1071,7 +1066,6 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
|
||||
|
||||
if (!mDocument
|
||||
|| mInPermitUnload
|
||||
|| mCallerIsClosingWindow
|
||||
|| mInPermitUnloadPrompt) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1247,16 +1241,12 @@ nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
|
||||
docShell->GetContentViewer(getter_AddRefs(cv));
|
||||
|
||||
if (cv) {
|
||||
cv->PermitUnloadInternal(aCallerClosesWindow, aShouldPrompt,
|
||||
aPermitUnload);
|
||||
cv->PermitUnloadInternal(aShouldPrompt, aPermitUnload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aCallerClosesWindow && *aPermitUnload)
|
||||
mCallerIsClosingWindow = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1274,35 +1264,6 @@ nsDocumentViewer::GetInPermitUnload(bool* aInEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocumentViewer::ResetCloseWindow()
|
||||
{
|
||||
mCallerIsClosingWindow = false;
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell(mContainer);
|
||||
if (docShell) {
|
||||
int32_t childCount;
|
||||
docShell->GetChildCount(&childCount);
|
||||
|
||||
for (int32_t i = 0; i < childCount; ++i) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> item;
|
||||
docShell->GetChildAt(i, getter_AddRefs(item));
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
|
||||
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsIContentViewer> cv;
|
||||
docShell->GetContentViewer(getter_AddRefs(cv));
|
||||
|
||||
if (cv) {
|
||||
cv->ResetCloseWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocumentViewer::PageHide(bool aIsUnload)
|
||||
{
|
||||
|
@ -61,6 +61,9 @@ if (AppConstants.MOZ_SAFE_BROWSING) {
|
||||
"resource://gre/modules/SafeBrowsing.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
@ -3491,6 +3494,10 @@ nsBrowserAccess.prototype = {
|
||||
isTabContentWindow: function(aWindow) {
|
||||
return BrowserApp.getBrowserForWindow(aWindow) != null;
|
||||
},
|
||||
|
||||
canClose() {
|
||||
return BrowserUtils.canCloseWindow(window);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
@ -195,6 +195,25 @@ this.FxAccountsOAuthClient.prototype = {
|
||||
};
|
||||
}
|
||||
|
||||
// if the message asked to close the tab
|
||||
if (data.closeWindow && target) {
|
||||
// for e10s reasons the best way is to use the TabBrowser to close the tab.
|
||||
let tabbrowser = target.getTabBrowser();
|
||||
|
||||
if (tabbrowser) {
|
||||
let tab = tabbrowser.getTabForBrowser(target);
|
||||
|
||||
if (tab) {
|
||||
tabbrowser.removeTab(tab);
|
||||
log.debug("OAuth flow closed the tab.");
|
||||
} else {
|
||||
log.debug("OAuth flow failed to close the tab. Tab not found in TabBrowser.");
|
||||
}
|
||||
} else {
|
||||
log.debug("OAuth flow failed to close the tab. TabBrowser not found.");
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
log.debug(err.message);
|
||||
if (this.onError) {
|
||||
@ -214,25 +233,6 @@ this.FxAccountsOAuthClient.prototype = {
|
||||
// onComplete will be called for this client only once
|
||||
// calling onComplete again will result in a failure of the OAuth flow
|
||||
this.tearDown();
|
||||
|
||||
// if the message asked to close the tab
|
||||
if (data.closeWindow && target) {
|
||||
// for e10s reasons the best way is to use the TabBrowser to close the tab.
|
||||
let tabbrowser = target.getTabBrowser();
|
||||
|
||||
if (tabbrowser) {
|
||||
let tab = tabbrowser.getTabForBrowser(target);
|
||||
|
||||
if (tab) {
|
||||
tabbrowser.removeTab(tab);
|
||||
log.debug("OAuth flow closed the tab.");
|
||||
} else {
|
||||
log.debug("OAuth flow failed to close the tab. Tab not found in TabBrowser.");
|
||||
}
|
||||
} else {
|
||||
log.debug("OAuth flow failed to close the tab. TabBrowser not found.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,5 @@ support-files =
|
||||
beforeunload.html
|
||||
|
||||
[browser_bug511456.js]
|
||||
skip-if = e10s # Bug ?????? - test touches content (uses a WindowWatcher in the parent process to try and observe content created alerts etc)
|
||||
[browser_bug537449.js]
|
||||
skip-if = e10s # Bug ?????? - test touches content (uses a WindowWatcher in the parent process to try and observe content created alerts etc)
|
||||
[browser_crash_detection.js]
|
||||
|
@ -577,6 +577,22 @@ var AutoCompletePopup = {
|
||||
}
|
||||
}
|
||||
|
||||
addMessageListener("InPermitUnload", msg => {
|
||||
let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
|
||||
sendAsyncMessage("InPermitUnload", {id: msg.data.id, inPermitUnload});
|
||||
});
|
||||
|
||||
addMessageListener("PermitUnload", msg => {
|
||||
sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "start"});
|
||||
|
||||
let permitUnload = true;
|
||||
if (docShell && docShell.contentViewer) {
|
||||
permitUnload = docShell.contentViewer.permitUnload();
|
||||
}
|
||||
|
||||
sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "end", permitUnload});
|
||||
});
|
||||
|
||||
// We may not get any responses to Browser:Init if the browser element
|
||||
// is torn down too quickly.
|
||||
var outerWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -1082,8 +1082,8 @@
|
||||
window.addEventListener("keypress", this, true);
|
||||
window.addEventListener("keyup", this, true);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="handleEvent">
|
||||
<parameter name="aEvent"/>
|
||||
@ -1236,6 +1236,30 @@
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="getInPermitUnload">
|
||||
<parameter name="aCallback"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!this.docShell || !this.docShell.contentViewer) {
|
||||
aCallback(false);
|
||||
return;
|
||||
}
|
||||
aCallback(this.docShell.contentViewer.inPermitUnload);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="permitUnload">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!this.docShell || !this.docShell.contentViewer) {
|
||||
return true;
|
||||
}
|
||||
return {permitUnload: this.docShell.contentViewer.permitUnload(), timedOut: false};
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<!-- This will go away if the binding has been removed for some reason. -->
|
||||
<field name="_alive">true</field>
|
||||
</implementation>
|
||||
|
@ -244,6 +244,89 @@
|
||||
|
||||
<field name="mDestroyed">false</field>
|
||||
|
||||
<field name="_permitUnloadId">0</field>
|
||||
|
||||
<method name="getInPermitUnload">
|
||||
<parameter name="aCallback"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let id = this._permitUnloadId++;
|
||||
let mm = this.messageManager;
|
||||
mm.sendAsyncMessage("InPermitUnload", {id});
|
||||
mm.addMessageListener("InPermitUnload", function listener(msg) {
|
||||
if (msg.data.id != id) {
|
||||
return;
|
||||
}
|
||||
aCallback(msg.data.inPermitUnload);
|
||||
});
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="permitUnload">
|
||||
<body>
|
||||
<![CDATA[
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
const kTimeout = 5000;
|
||||
|
||||
let finished = false;
|
||||
let responded = false;
|
||||
let permitUnload;
|
||||
let id = this._permitUnloadId++;
|
||||
let mm = this.messageManager;
|
||||
|
||||
let msgListener = msg => {
|
||||
if (msg.data.id != id) {
|
||||
return;
|
||||
}
|
||||
if (msg.data.kind == "start") {
|
||||
responded = true;
|
||||
return;
|
||||
}
|
||||
done(msg.data.permitUnload);
|
||||
};
|
||||
|
||||
let observer = subject => {
|
||||
if (subject == mm) {
|
||||
done(true);
|
||||
}
|
||||
};
|
||||
|
||||
function done(result) {
|
||||
finished = true;
|
||||
permitUnload = result;
|
||||
mm.removeMessageListener("PermitUnload", msgListener);
|
||||
Services.obs.removeObserver(observer, "message-manager-close");
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("PermitUnload", {id});
|
||||
mm.addMessageListener("PermitUnload", msgListener);
|
||||
Services.obs.addObserver(observer, "message-manager-close", false);
|
||||
|
||||
let timedOut = false;
|
||||
function timeout() {
|
||||
if (!responded) {
|
||||
timedOut = true;
|
||||
}
|
||||
|
||||
// Dispatch something to ensure that the main thread wakes up.
|
||||
Services.tm.mainThread.dispatch(function() {}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(timeout, kTimeout, timer.TYPE_ONE_SHOT);
|
||||
|
||||
while (!finished && !timedOut) {
|
||||
Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
|
||||
return {permitUnload, timedOut};
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
/*
|
||||
|
@ -387,5 +387,21 @@ this.BrowserUtils = {
|
||||
|
||||
return { text: selectionStr, docSelectionIsCollapsed: collapsed,
|
||||
linkURL: url ? url.spec : null, linkText: url ? linkText : "" };
|
||||
}
|
||||
},
|
||||
|
||||
// Iterates through every docshell in the window and calls PermitUnload.
|
||||
canCloseWindow(window) {
|
||||
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation);
|
||||
let node = docShell.QueryInterface(Ci.nsIDocShellTreeItem);
|
||||
for (let i = 0; i < node.childCount; ++i) {
|
||||
let docShell = node.getChildAt(i).QueryInterface(Ci.nsIDocShell);
|
||||
let contentViewer = docShell.contentViewer;
|
||||
if (contentViewer && !contentViewer.permitUnload()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user