Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
/* eslint no-unused-vars: ["error", {args: "none"}] */
// The maximum number of popup information we'll send to the parent.
const MAX_SENT_POPUPS = 15;
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
export class PopupBlockingChild extends JSWindowActorChild {
constructor() {
super();
this.weakDocStates = new WeakMap();
}
/**
* 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 "UnblockPopup": {
let i = msg.data.index;
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 == popupData.requestingDocument) {
dwi.open(
popupData.popupWindowURISpec,
popupData.popupWindowName,
popupData.popupWindowFeatures
);
}
}
break;
}
case "GetBlockedPopupList": {
let state = this.docState;
let length = Math.min(state.popupData.length, MAX_SENT_POPUPS);
let result = [];
for (let i = 0; i < length; ++i) {
let popup = state.popupData[i];
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);
}
result.push({
popupWindowURISpec,
});
}
return result;
}
}
return null;
}
handleEvent(event) {
switch (event.type) {
case "DOMPopupBlocked":
this.onPopupBlocked(event);
break;
case "pageshow": {
this.onPageShow(event);
break;
}
}
}
onPopupBlocked(event) {
if (event.target != this.document) {
return;
}
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: event.popupWindowFeatures,
popupWindowName: event.popupWindowName,
requestingWindow: event.requestingWindow,
requestingDocument: event.requestingWindow.document,
};
state.popupData.push(popup);
this.updateBlockedPopups(true);
}
onPageShow(event) {
if (event.target != this.document) {
return;
}
this.updateBlockedPopups(false);
}
updateBlockedPopups(shouldNotify) {
this.sendAsyncMessage("UpdateBlockedPopups", {
shouldNotify,
count: this.docState.popupData.length,
});
}
}
XPCOMUtils.defineLazyPreferenceGetter(
PopupBlockingChild,
"maxReportedPopups",
"privacy.popups.maxReported"
);