зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1576917 - Port PopupBlocker to JSWindowActors to make it Fission-compatible. r=NeilDeakin
This patch was started by Alex Vamvounis <a.vamvounis@gmail.com> and finished by Mike Conley <mconley@mozilla.com> Differential Revision: https://phabricator.services.mozilla.com/D53075 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
ef317eb299
Коммит
ea70cbeec9
|
@ -906,10 +906,7 @@ var gIdentityHandler = {
|
|||
|
||||
// Show blocked popup icon in the identity-box if popups are blocked
|
||||
// irrespective of popup permission capability value.
|
||||
if (
|
||||
gBrowser.selectedBrowser.blockedPopups &&
|
||||
gBrowser.selectedBrowser.blockedPopups.length
|
||||
) {
|
||||
if (gBrowser.selectedBrowser.popupBlocker.getBlockedPopupCount()) {
|
||||
let icon = permissionAnchors.popup;
|
||||
icon.setAttribute("showing", "true");
|
||||
}
|
||||
|
@ -1398,6 +1395,7 @@ var gIdentityHandler = {
|
|||
}
|
||||
}
|
||||
|
||||
let totalBlockedPopups = gBrowser.selectedBrowser.popupBlocker.getBlockedPopupCount();
|
||||
let hasBlockedPopupIndicator = false;
|
||||
for (let permission of permissions) {
|
||||
if (permission.id == "storage-access") {
|
||||
|
@ -1411,12 +1409,8 @@ var gIdentityHandler = {
|
|||
}
|
||||
this._permissionList.appendChild(item);
|
||||
|
||||
if (
|
||||
permission.id == "popup" &&
|
||||
gBrowser.selectedBrowser.blockedPopups &&
|
||||
gBrowser.selectedBrowser.blockedPopups.length
|
||||
) {
|
||||
this._createBlockedPopupIndicator();
|
||||
if (permission.id == "popup" && totalBlockedPopups) {
|
||||
this._createBlockedPopupIndicator(totalBlockedPopups);
|
||||
hasBlockedPopupIndicator = true;
|
||||
} else if (
|
||||
permission.id == "geo" &&
|
||||
|
@ -1426,11 +1420,7 @@ var gIdentityHandler = {
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
gBrowser.selectedBrowser.blockedPopups &&
|
||||
gBrowser.selectedBrowser.blockedPopups.length &&
|
||||
!hasBlockedPopupIndicator
|
||||
) {
|
||||
if (totalBlockedPopups && !hasBlockedPopupIndicator) {
|
||||
let permission = {
|
||||
id: "popup",
|
||||
state: SitePermissions.getDefault("popup"),
|
||||
|
@ -1438,7 +1428,7 @@ var gIdentityHandler = {
|
|||
};
|
||||
let item = this._createPermissionItem(permission);
|
||||
this._permissionList.appendChild(item);
|
||||
this._createBlockedPopupIndicator();
|
||||
this._createBlockedPopupIndicator(totalBlockedPopups);
|
||||
}
|
||||
|
||||
// Show a placeholder text if there's no permission and no reload hint.
|
||||
|
@ -1757,7 +1747,7 @@ var gIdentityHandler = {
|
|||
.appendChild(indicator);
|
||||
},
|
||||
|
||||
_createBlockedPopupIndicator() {
|
||||
_createBlockedPopupIndicator(aTotalBlockedPopups) {
|
||||
let indicator = document.createXULElement("hbox");
|
||||
indicator.setAttribute("class", "identity-popup-permission-item");
|
||||
indicator.setAttribute("align", "center");
|
||||
|
@ -1770,18 +1760,17 @@ var gIdentityHandler = {
|
|||
text.setAttribute("flex", "1");
|
||||
text.setAttribute("class", "identity-popup-permission-label");
|
||||
|
||||
let popupCount = gBrowser.selectedBrowser.blockedPopups.length;
|
||||
let messageBase = gNavigatorBundle.getString(
|
||||
"popupShowBlockedPopupsIndicatorText"
|
||||
);
|
||||
let message = PluralForm.get(popupCount, messageBase).replace(
|
||||
let message = PluralForm.get(aTotalBlockedPopups, messageBase).replace(
|
||||
"#1",
|
||||
popupCount
|
||||
aTotalBlockedPopups
|
||||
);
|
||||
text.textContent = message;
|
||||
|
||||
text.addEventListener("click", () => {
|
||||
gPopupBlockerObserver.showAllBlockedPopups(gBrowser.selectedBrowser);
|
||||
gBrowser.selectedBrowser.popupBlocker.unblockAllPopups();
|
||||
});
|
||||
|
||||
indicator.appendChild(icon);
|
||||
|
|
|
@ -948,10 +948,9 @@ var gPopupBlockerObserver = {
|
|||
|
||||
gIdentityHandler.refreshIdentityBlock();
|
||||
|
||||
if (
|
||||
!gBrowser.selectedBrowser.blockedPopups ||
|
||||
!gBrowser.selectedBrowser.blockedPopups.length
|
||||
) {
|
||||
let popupCount = gBrowser.selectedBrowser.popupBlocker.getBlockedPopupCount();
|
||||
|
||||
if (!popupCount) {
|
||||
// Hide the notification box (if it's visible).
|
||||
let notificationBox = gBrowser.getNotificationBox();
|
||||
let notification = notificationBox.getNotificationWithValue(
|
||||
|
@ -966,11 +965,10 @@ var gPopupBlockerObserver = {
|
|||
// Only show the notification again if we've not already shown it. Since
|
||||
// notifications are per-browser, we don't need to worry about re-adding
|
||||
// it.
|
||||
if (!gBrowser.selectedBrowser.blockedPopups.reported) {
|
||||
if (gBrowser.selectedBrowser.popupBlocker.shouldShowNotification) {
|
||||
if (Services.prefs.getBoolPref("privacy.popups.showBrowserMessage")) {
|
||||
var brandBundle = document.getElementById("bundle_brand");
|
||||
var brandShortName = brandBundle.getString("brandShortName");
|
||||
var popupCount = gBrowser.selectedBrowser.blockedPopups.length;
|
||||
|
||||
var stringKey =
|
||||
AppConstants.platform == "win"
|
||||
|
@ -1024,7 +1022,7 @@ var gPopupBlockerObserver = {
|
|||
|
||||
// Record the fact that we've reported this blocked popup, so we don't
|
||||
// show it again.
|
||||
gBrowser.selectedBrowser.blockedPopups.reported = true;
|
||||
gBrowser.selectedBrowser.popupBlocker.didShowNotification();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1035,7 +1033,7 @@ var gPopupBlockerObserver = {
|
|||
pm.addFromPrincipal(gBrowser.contentPrincipal, "popup", perm);
|
||||
|
||||
if (!shouldBlock) {
|
||||
this.showAllBlockedPopups(gBrowser.selectedBrowser);
|
||||
gBrowser.selectedBrowser.popupBlocker.unblockAllPopups();
|
||||
}
|
||||
|
||||
gBrowser.getNotificationBox().removeCurrentNotification();
|
||||
|
@ -1106,65 +1104,68 @@ var gPopupBlockerObserver = {
|
|||
);
|
||||
blockedPopupsSeparator.setAttribute("hidden", true);
|
||||
|
||||
gBrowser.selectedBrowser
|
||||
.retrieveListOfBlockedPopups()
|
||||
.then(blockedPopups => {
|
||||
let foundUsablePopupURI = false;
|
||||
if (blockedPopups) {
|
||||
for (let i = 0; i < blockedPopups.length; i++) {
|
||||
let blockedPopup = blockedPopups[i];
|
||||
browser.popupBlocker.getBlockedPopups().then(blockedPopups => {
|
||||
let foundUsablePopupURI = false;
|
||||
if (blockedPopups) {
|
||||
for (let i = 0; i < blockedPopups.length; i++) {
|
||||
let blockedPopup = blockedPopups[i];
|
||||
|
||||
// popupWindowURI will be null if the file picker popup is blocked.
|
||||
// xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
|
||||
if (!blockedPopup.popupWindowURIspec) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var popupURIspec = blockedPopup.popupWindowURIspec;
|
||||
|
||||
// Sometimes the popup URI that we get back from the blockedPopup
|
||||
// isn't useful (for instance, netscape.com's popup URI ends up
|
||||
// being "http://www.netscape.com", which isn't really the URI of
|
||||
// the popup they're trying to show). This isn't going to be
|
||||
// useful to the user, so we won't create a menu item for it.
|
||||
if (
|
||||
popupURIspec == "" ||
|
||||
popupURIspec == "about:blank" ||
|
||||
popupURIspec == "<self>" ||
|
||||
popupURIspec == uri.spec
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Because of the short-circuit above, we may end up in a situation
|
||||
// in which we don't have any usable popup addresses to show in
|
||||
// the menu, and therefore we shouldn't show the separator. However,
|
||||
// since we got past the short-circuit, we must've found at least
|
||||
// one usable popup URI and thus we'll turn on the separator later.
|
||||
foundUsablePopupURI = true;
|
||||
|
||||
var menuitem = document.createXULElement("menuitem");
|
||||
var label = gNavigatorBundle.getFormattedString(
|
||||
"popupShowPopupPrefix",
|
||||
[popupURIspec]
|
||||
);
|
||||
menuitem.setAttribute("label", label);
|
||||
menuitem.setAttribute(
|
||||
"oncommand",
|
||||
"gPopupBlockerObserver.showBlockedPopup(event);"
|
||||
);
|
||||
menuitem.setAttribute("popupReportIndex", i);
|
||||
menuitem.popupReportBrowser = browser;
|
||||
aEvent.target.appendChild(menuitem);
|
||||
// popupWindowURI will be null if the file picker popup is blocked.
|
||||
// xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
|
||||
if (!blockedPopup.popupWindowURISpec) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Show the separator if we added any
|
||||
// showable popup addresses to the menu.
|
||||
if (foundUsablePopupURI) {
|
||||
blockedPopupsSeparator.removeAttribute("hidden");
|
||||
var popupURIspec = blockedPopup.popupWindowURISpec;
|
||||
|
||||
// Sometimes the popup URI that we get back from the blockedPopup
|
||||
// isn't useful (for instance, netscape.com's popup URI ends up
|
||||
// being "http://www.netscape.com", which isn't really the URI of
|
||||
// the popup they're trying to show). This isn't going to be
|
||||
// useful to the user, so we won't create a menu item for it.
|
||||
if (
|
||||
popupURIspec == "" ||
|
||||
popupURIspec == "about:blank" ||
|
||||
popupURIspec == "<self>" ||
|
||||
popupURIspec == uri.spec
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Because of the short-circuit above, we may end up in a situation
|
||||
// in which we don't have any usable popup addresses to show in
|
||||
// the menu, and therefore we shouldn't show the separator. However,
|
||||
// since we got past the short-circuit, we must've found at least
|
||||
// one usable popup URI and thus we'll turn on the separator later.
|
||||
foundUsablePopupURI = true;
|
||||
|
||||
var menuitem = document.createXULElement("menuitem");
|
||||
var label = gNavigatorBundle.getFormattedString(
|
||||
"popupShowPopupPrefix",
|
||||
[popupURIspec]
|
||||
);
|
||||
menuitem.setAttribute("label", label);
|
||||
menuitem.setAttribute(
|
||||
"oncommand",
|
||||
"gPopupBlockerObserver.showBlockedPopup(event);"
|
||||
);
|
||||
menuitem.setAttribute("popupReportIndex", i);
|
||||
menuitem.setAttribute(
|
||||
"popupInnerWindowId",
|
||||
blockedPopup.innerWindowId
|
||||
);
|
||||
menuitem.browsingContext = blockedPopup.browsingContext;
|
||||
menuitem.popupReportBrowser = browser;
|
||||
aEvent.target.appendChild(menuitem);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
// Show the separator if we added any
|
||||
// showable popup addresses to the menu.
|
||||
if (foundUsablePopupURI) {
|
||||
blockedPopupsSeparator.removeAttribute("hidden");
|
||||
}
|
||||
}, null);
|
||||
},
|
||||
|
||||
onPopupHiding(aEvent) {
|
||||
|
@ -1177,20 +1178,16 @@ var gPopupBlockerObserver = {
|
|||
},
|
||||
|
||||
showBlockedPopup(aEvent) {
|
||||
var target = aEvent.target;
|
||||
var popupReportIndex = target.getAttribute("popupReportIndex");
|
||||
let target = aEvent.target;
|
||||
let browsingContext = target.browsingContext;
|
||||
let innerWindowId = target.getAttribute("popupInnerWindowId");
|
||||
let popupReportIndex = target.getAttribute("popupReportIndex");
|
||||
let browser = target.popupReportBrowser;
|
||||
browser.unblockPopup(popupReportIndex);
|
||||
},
|
||||
|
||||
showAllBlockedPopups(aBrowser) {
|
||||
aBrowser.retrieveListOfBlockedPopups().then(popups => {
|
||||
for (let i = 0; i < popups.length; i++) {
|
||||
if (popups[i].popupWindowURIspec) {
|
||||
aBrowser.unblockPopup(i);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
browser.popupBlocker.unblockPopup(
|
||||
browsingContext,
|
||||
innerWindowId,
|
||||
popupReportIndex
|
||||
);
|
||||
},
|
||||
|
||||
editPopupSettings() {
|
||||
|
|
|
@ -1057,11 +1057,10 @@
|
|||
|
||||
this._appendStatusPanel();
|
||||
|
||||
if (
|
||||
(oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
|
||||
(!oldBrowser.blockedPopups && newBrowser.blockedPopups)
|
||||
) {
|
||||
newBrowser.updateBlockedPopups();
|
||||
let oldBrowserPopupsBlocked = oldBrowser.popupBlocker.getBlockedPopupCount();
|
||||
let newBrowserPopupsBlocked = newBrowser.popupBlocker.getBlockedPopupCount();
|
||||
if (oldBrowserPopupsBlocked != newBrowserPopupsBlocked) {
|
||||
newBrowser.popupBlocker.updateBlockedPopupsUI();
|
||||
}
|
||||
|
||||
// Update the URL bar.
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
[browser_popupUI.js]
|
||||
[browser_popup_blocker.js]
|
||||
support-files =
|
||||
|
|
|
@ -71,27 +71,13 @@ add_task(async function test_opening_blocked_popups() {
|
|||
|
||||
// Show the menu.
|
||||
let popupShown = BrowserTestUtils.waitForEvent(window, "popupshown");
|
||||
let popupFilled = BrowserTestUtils.waitForMessage(
|
||||
gBrowser.selectedBrowser.messageManager,
|
||||
"PopupBlocking:ReplyGetBlockedPopupList"
|
||||
);
|
||||
let popupFilled = waitForBlockedPopups(2);
|
||||
notification.querySelector("button").doCommand();
|
||||
let popup_event = await popupShown;
|
||||
let menu = popup_event.target;
|
||||
is(menu.id, "blockedPopupOptions", "Blocked popup menu shown");
|
||||
|
||||
await popupFilled;
|
||||
// The menu is filled on the same message that we waited for, so let's ensure that it
|
||||
// had a chance of running before this test code.
|
||||
await new Promise(resolve => executeSoon(resolve));
|
||||
|
||||
// Check the menu contents.
|
||||
let sep = menu.querySelector("menuseparator");
|
||||
let popupCount = 0;
|
||||
for (let i = sep.nextElementSibling; i; i = i.nextElementSibling) {
|
||||
popupCount++;
|
||||
}
|
||||
is(popupCount, 2, "Two popups were blocked");
|
||||
|
||||
// Pressing "allow" should open all blocked popups.
|
||||
let popupTabs = [];
|
||||
|
|
|
@ -36,44 +36,45 @@ add_task(async function test_opening_blocked_popups() {
|
|||
() =>
|
||||
(notification = gBrowser
|
||||
.getNotificationBox()
|
||||
.getNotificationWithValue("popup-blocked"))
|
||||
.getNotificationWithValue("popup-blocked")),
|
||||
"Waiting for the popup-blocked notification."
|
||||
);
|
||||
|
||||
ok(notification, "Should have notification.");
|
||||
|
||||
await ContentTask.spawn(tab.linkedBrowser, baseURL, async function(uri) {
|
||||
let pageHideHappened = BrowserTestUtils.waitForContentEvent(
|
||||
tab.linkedBrowser,
|
||||
"pagehide",
|
||||
true
|
||||
);
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [baseURL], async function(uri) {
|
||||
let iframe = content.document.createElement("iframe");
|
||||
let pageHideHappened = ContentTaskUtils.waitForEvent(
|
||||
this,
|
||||
"pagehide",
|
||||
true
|
||||
);
|
||||
content.document.body.appendChild(iframe);
|
||||
iframe.src = uri;
|
||||
await pageHideHappened;
|
||||
});
|
||||
|
||||
await pageHideHappened;
|
||||
notification = gBrowser
|
||||
.getNotificationBox()
|
||||
.getNotificationWithValue("popup-blocked");
|
||||
ok(notification, "Should still have notification");
|
||||
|
||||
pageHideHappened = BrowserTestUtils.waitForContentEvent(
|
||||
tab.linkedBrowser,
|
||||
"pagehide",
|
||||
true
|
||||
);
|
||||
// Now navigate the subframe.
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, async function() {
|
||||
let pageHideHappened = ContentTaskUtils.waitForEvent(
|
||||
this,
|
||||
"pagehide",
|
||||
true
|
||||
);
|
||||
await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
|
||||
content.document.getElementById(
|
||||
"popupframe"
|
||||
).contentDocument.location.href = "about:blank";
|
||||
await pageHideHappened;
|
||||
});
|
||||
|
||||
await pageHideHappened;
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() =>
|
||||
!gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked")
|
||||
!gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
|
||||
"Notification should go away"
|
||||
);
|
||||
ok(
|
||||
!gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
async function waitForBlockedPopups(numberOfPopups) {
|
||||
let menupopup = document.getElementById("blockedPopupOptions");
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
let popups = menupopup.querySelectorAll("[popupReportIndex]");
|
||||
return popups.length == numberOfPopups;
|
||||
}, `Waiting for ${numberOfPopups} popups`);
|
||||
}
|
|
@ -5579,13 +5579,7 @@ void nsGlobalWindowOuter::FireAbuseEvents(
|
|||
const nsAString& aPopupURL, const nsAString& aPopupWindowName,
|
||||
const nsAString& aPopupWindowFeatures) {
|
||||
// fetch the URI of the window requesting the opened window
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window = GetInProcessTop();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<Document> topDoc = window->GetDoc();
|
||||
nsCOMPtr<Document> currentDoc = GetDoc();
|
||||
nsCOMPtr<nsIURI> popupURI;
|
||||
|
||||
// build the URI of the would-have-been popup window
|
||||
|
@ -5605,7 +5599,7 @@ void nsGlobalWindowOuter::FireAbuseEvents(
|
|||
getter_AddRefs(popupURI));
|
||||
|
||||
// fire an event block full of informative URIs
|
||||
FirePopupBlockedEvent(topDoc, popupURI, aPopupWindowName,
|
||||
FirePopupBlockedEvent(currentDoc, popupURI, aPopupWindowName,
|
||||
aPopupWindowFeatures);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,153 +7,147 @@
|
|||
|
||||
var EXPORTED_SYMBOLS = ["PopupBlockingChild"];
|
||||
|
||||
const { ActorChild } = ChromeUtils.import(
|
||||
"resource://gre/modules/ActorChild.jsm"
|
||||
);
|
||||
// The maximum number of popup information we'll send to the parent.
|
||||
const MAX_SENT_POPUPS = 15;
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
class PopupBlockingChild extends ActorChild {
|
||||
constructor(dispatcher) {
|
||||
super(dispatcher);
|
||||
class PopupBlockingChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
this.weakDocStates = new WeakMap();
|
||||
}
|
||||
|
||||
this.popupData = null;
|
||||
this.popupDataInternal = null;
|
||||
actorCreated() {
|
||||
this.contentWindow.addEventListener("pageshow", this);
|
||||
}
|
||||
|
||||
this.mm.addEventListener("pageshow", this, true);
|
||||
this.mm.addEventListener("pagehide", this, true);
|
||||
willDestroy() {
|
||||
this.contentWindow.removeEventListener("pageshow", this);
|
||||
}
|
||||
|
||||
this.mm.addMessageListener("PopupBlocking:UnblockPopup", this);
|
||||
this.mm.addMessageListener("PopupBlocking:GetBlockedPopupList", this);
|
||||
/**
|
||||
* Returns the state for the current document referred to via
|
||||
* this.document. If no such state exists, creates it, stores it
|
||||
* and returns it.
|
||||
*/
|
||||
get docState() {
|
||||
let state = this.weakDocStates.get(this.document);
|
||||
if (!state) {
|
||||
state = {
|
||||
popupData: [],
|
||||
};
|
||||
this.weakDocStates.set(this.document, state);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "PopupBlocking:UnblockPopup": {
|
||||
case "UnblockPopup": {
|
||||
let i = msg.data.index;
|
||||
if (this.popupData && this.popupData[i]) {
|
||||
let data = this.popupData[i];
|
||||
let internals = this.popupDataInternal[i];
|
||||
let dwi = internals.requestingWindow;
|
||||
let state = this.docState;
|
||||
let popupData = state.popupData[i];
|
||||
if (popupData) {
|
||||
let dwi = popupData.requestingWindow;
|
||||
|
||||
// If we have a requesting window and the requesting document is
|
||||
// still the current document, open the popup.
|
||||
if (dwi && dwi.document == internals.requestingDocument) {
|
||||
if (dwi && dwi.document == popupData.requestingDocument) {
|
||||
dwi.open(
|
||||
data.popupWindowURIspec,
|
||||
data.popupWindowName,
|
||||
data.popupWindowFeatures
|
||||
popupData.popupWindowURISpec,
|
||||
popupData.popupWindowName,
|
||||
popupData.popupWindowFeatures
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "PopupBlocking:GetBlockedPopupList": {
|
||||
let popupData = [];
|
||||
let length = this.popupData ? this.popupData.length : 0;
|
||||
case "GetBlockedPopupList": {
|
||||
let state = this.docState;
|
||||
let length = Math.min(state.popupData.length, MAX_SENT_POPUPS);
|
||||
|
||||
// Limit 15 popup URLs to be reported through the UI
|
||||
length = Math.min(length, 15);
|
||||
let result = [];
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
let popupWindowURIspec = this.popupData[i].popupWindowURIspec;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let popup = state.popupData[i];
|
||||
|
||||
if (popupWindowURIspec == this.mm.content.location.href) {
|
||||
popupWindowURIspec = "<self>";
|
||||
let popupWindowURISpec = popup.popupWindowURISpec;
|
||||
|
||||
if (this.contentWindow.location.href == popupWindowURISpec) {
|
||||
popupWindowURISpec = "<self>";
|
||||
} else {
|
||||
// Limit 500 chars to be sent because the URI will be cropped
|
||||
// by the UI anyway, and data: URIs can be significantly larger.
|
||||
popupWindowURIspec = popupWindowURIspec.substring(0, 500);
|
||||
popupWindowURISpec = popupWindowURISpec.substring(0, 500);
|
||||
}
|
||||
|
||||
popupData.push({ popupWindowURIspec });
|
||||
result.push({
|
||||
popupWindowURISpec,
|
||||
});
|
||||
}
|
||||
|
||||
this.mm.sendAsyncMessage("PopupBlocking:ReplyGetBlockedPopupList", {
|
||||
popupData,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "DOMPopupBlocked":
|
||||
this.onPopupBlocked(event);
|
||||
break;
|
||||
case "pageshow": {
|
||||
this.onPageShow(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(ev) {
|
||||
switch (ev.type) {
|
||||
case "DOMPopupBlocked":
|
||||
return this.onPopupBlocked(ev);
|
||||
case "pageshow":
|
||||
return this._removeIrrelevantPopupData();
|
||||
case "pagehide":
|
||||
return this._removeIrrelevantPopupData(ev.target);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
onPopupBlocked(ev) {
|
||||
if (!this.popupData) {
|
||||
this.popupData = [];
|
||||
this.popupDataInternal = [];
|
||||
}
|
||||
|
||||
// Avoid spamming the parent process with too many blocked popups.
|
||||
if (this.popupData.length >= PopupBlockingChild.maxReportedPopups) {
|
||||
onPopupBlocked(event) {
|
||||
if (event.target != this.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = {
|
||||
popupWindowURIspec: ev.popupWindowURI
|
||||
? ev.popupWindowURI.spec
|
||||
let state = this.docState;
|
||||
|
||||
// Avoid spamming the parent process with too many blocked popups.
|
||||
if (state.popupData.length >= PopupBlockingChild.maxReportedPopups) {
|
||||
return;
|
||||
}
|
||||
|
||||
let popup = {
|
||||
popupWindowURISpec: event.popupWindowURI
|
||||
? event.popupWindowURI.spec
|
||||
: "about:blank",
|
||||
popupWindowFeatures: ev.popupWindowFeatures,
|
||||
popupWindowName: ev.popupWindowName,
|
||||
popupWindowFeatures: event.popupWindowFeatures,
|
||||
popupWindowName: event.popupWindowName,
|
||||
requestingWindow: event.requestingWindow,
|
||||
requestingDocument: event.requestingWindow.document,
|
||||
};
|
||||
|
||||
let internals = {
|
||||
requestingWindow: ev.requestingWindow,
|
||||
requestingDocument: ev.requestingWindow.document,
|
||||
};
|
||||
|
||||
this.popupData.push(obj);
|
||||
this.popupDataInternal.push(internals);
|
||||
state.popupData.push(popup);
|
||||
this.updateBlockedPopups(true);
|
||||
}
|
||||
|
||||
_removeIrrelevantPopupData(removedDoc = null) {
|
||||
if (this.popupData) {
|
||||
let i = 0;
|
||||
let oldLength = this.popupData.length;
|
||||
while (i < this.popupData.length) {
|
||||
let { requestingWindow, requestingDocument } = this.popupDataInternal[
|
||||
i
|
||||
];
|
||||
// Filter out irrelevant reports.
|
||||
if (
|
||||
requestingWindow &&
|
||||
requestingWindow.document == requestingDocument &&
|
||||
requestingDocument != removedDoc
|
||||
) {
|
||||
i++;
|
||||
} else {
|
||||
this.popupData.splice(i, 1);
|
||||
this.popupDataInternal.splice(i, 1);
|
||||
}
|
||||
}
|
||||
if (!this.popupData.length) {
|
||||
this.popupData = null;
|
||||
this.popupDataInternal = null;
|
||||
}
|
||||
if (!this.popupData || oldLength > this.popupData.length) {
|
||||
this.updateBlockedPopups(false);
|
||||
}
|
||||
onPageShow(event) {
|
||||
if (event.target != this.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateBlockedPopups(false);
|
||||
}
|
||||
|
||||
updateBlockedPopups(freshPopup) {
|
||||
this.mm.sendAsyncMessage("PopupBlocking:UpdateBlockedPopups", {
|
||||
count: this.popupData ? this.popupData.length : 0,
|
||||
freshPopup,
|
||||
updateBlockedPopups(shouldNotify) {
|
||||
this.sendAsyncMessage("UpdateBlockedPopups", {
|
||||
shouldNotify,
|
||||
count: this.docState.popupData.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
/* 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/. */
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["PopupBlocker", "PopupBlockingParent"];
|
||||
|
||||
/**
|
||||
* This class manages all popup blocking operations on a <xul:browser>, including
|
||||
* notifying the UI about updates to the blocked popups, and allowing popups to
|
||||
* be unblocked.
|
||||
*/
|
||||
class PopupBlocker {
|
||||
constructor(browser) {
|
||||
this._browser = browser;
|
||||
this._allBlockedPopupCounts = new WeakMap();
|
||||
this._shouldShowNotification = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not there are new blocked popups for the associated
|
||||
* <xul:browser> that the user might need to be notified about.
|
||||
*/
|
||||
get shouldShowNotification() {
|
||||
return this._shouldShowNotification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called by the UI when the user has been notified about blocked
|
||||
* popups for the associated <xul:browser>.
|
||||
*/
|
||||
didShowNotification() {
|
||||
this._shouldShowNotification = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously returns the most recent count of blocked popups for
|
||||
* the associated <xul:browser>.
|
||||
*
|
||||
* @return {Number}
|
||||
* The total number of blocked popups for this <xul:browser>.
|
||||
*/
|
||||
getBlockedPopupCount() {
|
||||
let totalBlockedPopups = 0;
|
||||
|
||||
let contextsToVisit = [this._browser.browsingContext];
|
||||
while (contextsToVisit.length) {
|
||||
let currentBC = contextsToVisit.pop();
|
||||
let windowGlobal = currentBC.currentWindowGlobal;
|
||||
|
||||
if (!windowGlobal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let popupCountForGlobal =
|
||||
this._allBlockedPopupCounts.get(windowGlobal) || 0;
|
||||
totalBlockedPopups += popupCountForGlobal;
|
||||
contextsToVisit.push(...currentBC.getChildren());
|
||||
}
|
||||
|
||||
return totalBlockedPopups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously retrieve information about the popups that have
|
||||
* been blocked for the associated <xul:browser>. This information
|
||||
* can be used to unblock those popups.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves {Array}
|
||||
* When the blocked popup information has been gathered,
|
||||
* resolves with an Array of Objects with the following properties:
|
||||
*
|
||||
* browsingContext {BrowsingContext}
|
||||
* The BrowsingContext that the popup was blocked for.
|
||||
*
|
||||
* innerWindowId {Number}
|
||||
* The inner window ID for the blocked popup. This is used to differentiate
|
||||
* popups that were blocked from one page load to the next.
|
||||
*
|
||||
* popupWindowURISpec {String}
|
||||
* A string representing part or all of the URI that tried to be opened in a
|
||||
* popup.
|
||||
*/
|
||||
async getBlockedPopups() {
|
||||
let contextsToVisit = [this._browser.browsingContext];
|
||||
let result = [];
|
||||
while (contextsToVisit.length) {
|
||||
let currentBC = contextsToVisit.pop();
|
||||
let windowGlobal = currentBC.currentWindowGlobal;
|
||||
|
||||
if (!windowGlobal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let popupCountForGlobal =
|
||||
this._allBlockedPopupCounts.get(windowGlobal) || 0;
|
||||
if (popupCountForGlobal) {
|
||||
let actor = windowGlobal.getActor("PopupBlocking");
|
||||
let popups = await actor.sendQuery("GetBlockedPopupList");
|
||||
|
||||
for (let popup of popups) {
|
||||
if (!popup.popupWindowURISpec) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push({
|
||||
browsingContext: currentBC,
|
||||
innerWindowId: windowGlobal.innerWindowId,
|
||||
popupWindowURISpec: popup.popupWindowURISpec,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
contextsToVisit.push(...currentBC.getChildren());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unblocks a popup that had been blocked. The information passed should
|
||||
* come from the list of blocked popups returned via getBlockedPopups().
|
||||
*
|
||||
* Unblocking a popup causes that popup to open.
|
||||
*
|
||||
* @param browsingContext {BrowsingContext}
|
||||
* The BrowsingContext that the popup was blocked for.
|
||||
*
|
||||
* @param innerWindowId {Number}
|
||||
* The inner window ID for the blocked popup. This is used to differentiate popups
|
||||
* that were blocked from one page load to the next.
|
||||
*
|
||||
* @param popupIndex {Number}
|
||||
* The index of the entry in the Array returned by getBlockedPopups().
|
||||
*/
|
||||
unblockPopup(browsingContext, innerWindowId, popupIndex) {
|
||||
let popupFrame = browsingContext.top.embedderElement;
|
||||
let popupBrowser = popupFrame.outerBrowser
|
||||
? popupFrame.outerBrowser
|
||||
: popupFrame;
|
||||
|
||||
if (this._browser != popupBrowser) {
|
||||
throw new Error(
|
||||
"Attempting to unblock popup in a BrowsingContext no longer hosted in this browser."
|
||||
);
|
||||
}
|
||||
|
||||
let windowGlobal = browsingContext.currentWindowGlobal;
|
||||
|
||||
if (!windowGlobal || windowGlobal.innerWindowId != innerWindowId) {
|
||||
// The inner window has moved on since the user clicked on
|
||||
// the blocked popups dropdown, so we'll just exit silently.
|
||||
return;
|
||||
}
|
||||
|
||||
let actor = browsingContext.currentWindowGlobal.getActor("PopupBlocking");
|
||||
actor.sendAsyncMessage("UnblockPopup", { index: popupIndex });
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through the most recent list of blocked popups for the associated
|
||||
* <xul:browser> and unblocks all of them. Unblocking a popup causes the popup
|
||||
* to open.
|
||||
*/
|
||||
async unblockAllPopups() {
|
||||
let popups = await this.getBlockedPopups();
|
||||
for (let i = 0; i < popups.length; ++i) {
|
||||
let popup = popups[i];
|
||||
this.unblockPopup(popup.browsingContext, popup.innerWindowId, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a DOMUpdateBlockedPopups chrome-only event so that the UI can
|
||||
* update itself to represent the current state of popup blocking for
|
||||
* the associated <xul:browser>.
|
||||
*/
|
||||
updateBlockedPopupsUI() {
|
||||
let event = this._browser.ownerDocument.createEvent("Events");
|
||||
event.initEvent("DOMUpdateBlockedPopups", true, true);
|
||||
this._browser.dispatchEvent(event);
|
||||
}
|
||||
|
||||
/** Private methods **/
|
||||
|
||||
/**
|
||||
* Updates the current popup count for a particular BrowsingContext based
|
||||
* on messages from the underlying process.
|
||||
*
|
||||
* This should only be called by a PopupBlockingParent instance.
|
||||
*
|
||||
* @param browsingContext {BrowsingContext}
|
||||
* The BrowsingContext to update the internal blocked popup count for.
|
||||
*
|
||||
* @param blockedPopupData {Object}
|
||||
* An Object representing information about how many popups are blocked
|
||||
* for the BrowsingContext. The Object has the following properties:
|
||||
*
|
||||
* count {Number}
|
||||
* The total number of blocked popups for the BrowsingContext.
|
||||
*
|
||||
* shouldNotify {Boolean}
|
||||
* Whether or not the list of blocked popups has changed in such a way that
|
||||
* the UI should be updated about it.
|
||||
*/
|
||||
_updateBlockedPopupEntries(browsingContext, blockedPopupData) {
|
||||
let windowGlobal = browsingContext.currentWindowGlobal;
|
||||
let { count, shouldNotify } = blockedPopupData;
|
||||
|
||||
if (!this.shouldShowNotification && shouldNotify) {
|
||||
this._shouldShowNotification = true;
|
||||
}
|
||||
|
||||
if (windowGlobal) {
|
||||
this._allBlockedPopupCounts.set(windowGlobal, count);
|
||||
}
|
||||
|
||||
this.updateBlockedPopupsUI();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To keep things properly encapsulated, these should only be instantiated via
|
||||
* the PopupBlocker class for a particular <xul:browser>.
|
||||
*
|
||||
* Instantiated for a WindowGlobalParent for a BrowsingContext in one of two cases:
|
||||
*
|
||||
* 1. One or more popups have been blocked for the underlying frame represented
|
||||
* by the WindowGlobalParent.
|
||||
*
|
||||
* 2. Something in the parent process is querying a frame for information about
|
||||
* any popups that may have been blocked inside of it.
|
||||
*/
|
||||
class PopupBlockingParent extends JSWindowActorParent {
|
||||
didDestroy() {
|
||||
this.updatePopupCountForBrowser({ count: 0, shouldNotify: false });
|
||||
}
|
||||
|
||||
receiveMessage(message) {
|
||||
if (message.name == "UpdateBlockedPopups") {
|
||||
this.updatePopupCountForBrowser({
|
||||
count: message.data.count,
|
||||
shouldNotify: message.data.shouldNotify,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the PopupBlocker for the <xul:browser> associated with this
|
||||
* PopupBlockingParent with the most recent count of blocked popups.
|
||||
*
|
||||
* @param data {Object}
|
||||
* An Object with the following properties:
|
||||
*
|
||||
* count {Number}:
|
||||
* The number of blocked popups for the underlying document.
|
||||
*
|
||||
* shouldNotify {Boolean}:
|
||||
* Whether or not the list of blocked popups has changed in such a way that
|
||||
* the UI should be updated about it.
|
||||
*/
|
||||
updatePopupCountForBrowser(data) {
|
||||
let browser = this.browsingContext.top.embedderElement;
|
||||
if (!browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (browser.outerBrowser) {
|
||||
browser = browser.outerBrowser; // handle RDM mode
|
||||
}
|
||||
|
||||
browser.popupBlocker._updateBlockedPopupEntries(this.browsingContext, data);
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ FINAL_TARGET_FILES.actors += [
|
|||
'KeyPressEventModelCheckerChild.jsm',
|
||||
'PictureInPictureChild.jsm',
|
||||
'PopupBlockingChild.jsm',
|
||||
'PopupBlockingParent.jsm',
|
||||
'PrintingChild.jsm',
|
||||
'PurgeSessionHistoryChild.jsm',
|
||||
'SelectChild.jsm',
|
||||
|
|
|
@ -34,6 +34,18 @@
|
|||
"resource://gre/modules/RemoteWebNavigation.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
LazyModules,
|
||||
"PopupBlocker",
|
||||
"resource://gre/actors/PopupBlockingParent.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
LazyModules,
|
||||
"XPCOMUtils",
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
const elementsToDestroyOnUnload = new Set();
|
||||
|
||||
window.addEventListener(
|
||||
|
@ -95,6 +107,10 @@
|
|||
// between calls to destroy().
|
||||
this.progressListeners = [];
|
||||
|
||||
LazyModules.XPCOMUtils.defineLazyGetter(this, "popupBlocker", () => {
|
||||
return new LazyModules.PopupBlocker(this);
|
||||
});
|
||||
|
||||
this.addEventListener(
|
||||
"keypress",
|
||||
event => {
|
||||
|
@ -341,8 +357,6 @@
|
|||
|
||||
this._mStrBundle = null;
|
||||
|
||||
this.blockedPopups = null;
|
||||
|
||||
this._audioMuted = false;
|
||||
|
||||
this._hasAnyPlayingMediaBeenBlocked = false;
|
||||
|
@ -1078,38 +1092,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
updateBlockedPopups() {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("DOMUpdateBlockedPopups", true, true);
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
retrieveListOfBlockedPopups() {
|
||||
this.messageManager.sendAsyncMessage(
|
||||
"PopupBlocking:GetBlockedPopupList",
|
||||
null
|
||||
);
|
||||
return new Promise(resolve => {
|
||||
let self = this;
|
||||
this.messageManager.addMessageListener(
|
||||
"PopupBlocking:ReplyGetBlockedPopupList",
|
||||
function replyReceived(msg) {
|
||||
self.messageManager.removeMessageListener(
|
||||
"PopupBlocking:ReplyGetBlockedPopupList",
|
||||
replyReceived
|
||||
);
|
||||
resolve(msg.data.popupData);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
unblockPopup(aPopupIndex) {
|
||||
this.messageManager.sendAsyncMessage("PopupBlocking:UnblockPopup", {
|
||||
index: aPopupIndex,
|
||||
});
|
||||
}
|
||||
|
||||
audioPlaybackStarted() {
|
||||
if (this._audioMuted) {
|
||||
return;
|
||||
|
@ -1339,10 +1321,6 @@
|
|||
}
|
||||
|
||||
if (this.messageManager) {
|
||||
this.messageManager.addMessageListener(
|
||||
"PopupBlocking:UpdateBlockedPopups",
|
||||
this
|
||||
);
|
||||
this.messageManager.addMessageListener(
|
||||
"UnselectedTabHover:Toggle",
|
||||
this
|
||||
|
@ -1403,15 +1381,6 @@
|
|||
_receiveMessage(aMessage) {
|
||||
let data = aMessage.data;
|
||||
switch (aMessage.name) {
|
||||
case "PopupBlocking:UpdateBlockedPopups": {
|
||||
this.blockedPopups = {
|
||||
length: data.count,
|
||||
reported: !data.freshPopup,
|
||||
};
|
||||
|
||||
this.updateBlockedPopups();
|
||||
break;
|
||||
}
|
||||
case "UnselectedTabHover:Toggle":
|
||||
this._shouldSendUnselectedTabHover = data.enable
|
||||
? ++this._unselectedTabHoverMessageListenerCount > 0
|
||||
|
|
|
@ -263,6 +263,19 @@ let ACTORS = {
|
|||
allFrames: true,
|
||||
},
|
||||
|
||||
PopupBlocking: {
|
||||
parent: {
|
||||
moduleURI: "resource://gre/actors/PopupBlockingParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource://gre/actors/PopupBlockingChild.jsm",
|
||||
events: {
|
||||
DOMPopupBlocked: { capture: true },
|
||||
},
|
||||
},
|
||||
allFrames: true,
|
||||
},
|
||||
|
||||
PurgeSessionHistory: {
|
||||
child: {
|
||||
moduleURI: "resource://gre/actors/PurgeSessionHistoryChild.jsm",
|
||||
|
@ -455,15 +468,6 @@ let LEGACY_ACTORS = {
|
|||
},
|
||||
},
|
||||
|
||||
PopupBlocking: {
|
||||
child: {
|
||||
module: "resource://gre/actors/PopupBlockingChild.jsm",
|
||||
events: {
|
||||
DOMPopupBlocked: { capture: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Printing: {
|
||||
child: {
|
||||
module: "resource://gre/actors/PrintingChild.jsm",
|
||||
|
|
Загрузка…
Ссылка в новой задаче