summaryrefslogtreecommitdiffstats
path: root/toolkit/actors/PopupBlockingChild.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/actors/PopupBlockingChild.sys.mjs147
1 files changed, 147 insertions, 0 deletions
diff --git a/toolkit/actors/PopupBlockingChild.sys.mjs b/toolkit/actors/PopupBlockingChild.sys.mjs
new file mode 100644
index 0000000000..053f9683c5
--- /dev/null
+++ b/toolkit/actors/PopupBlockingChild.sys.mjs
@@ -0,0 +1,147 @@
+/* -*- 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/. */
+
+/* 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"
+);