Bug 1845017 - Disable the TestPHCExhaustion test r=glandium
[gecko.git] / browser / actors / PageStyleChild.sys.mjs
blobf7d08bab08d3307a128ee5e77cb125581cc3cf19
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 export class PageStyleChild extends JSWindowActorChild {
6   actorCreated() {
7     // C++ can create the actor and call us here once an "interesting" link
8     // element gets added to the DOM. If pageload hasn't finished yet, just
9     // wait for that by doing nothing; the actor registration event
10     // listeners will ensure we get the pageshow event.
11     // It is also possible we get created in response to the parent
12     // sending us a message - in that case, it's still worth doing the
13     // same things here:
14     if (!this.browsingContext || !this.browsingContext.associatedWindow) {
15       return;
16     }
17     let { document } = this.browsingContext.associatedWindow;
18     if (document.readyState != "complete") {
19       return;
20     }
21     // If we've already seen a pageshow, send stylesheets now:
22     this.#collectAndSendSheets();
23   }
25   handleEvent(event) {
26     if (event?.type != "pageshow") {
27       throw new Error("Unexpected event!");
28     }
30     // On page show, tell the parent all of the stylesheets this document
31     // has. If we are in the topmost browsing context, delete the stylesheets
32     // from the previous page.
33     if (this.browsingContext.top === this.browsingContext) {
34       this.sendAsyncMessage("PageStyle:Clear");
35     }
37     this.#collectAndSendSheets();
38   }
40   receiveMessage(msg) {
41     switch (msg.name) {
42       // Sent when the page's enabled style sheet is changed.
43       case "PageStyle:Switch":
44         if (this.browsingContext.top == this.browsingContext) {
45           this.browsingContext.authorStyleDisabledDefault = false;
46         }
47         this.docShell.contentViewer.authorStyleDisabled = false;
48         this._switchStylesheet(msg.data.title);
49         break;
50       // Sent when "No Style" is chosen.
51       case "PageStyle:Disable":
52         if (this.browsingContext.top == this.browsingContext) {
53           this.browsingContext.authorStyleDisabledDefault = true;
54         }
55         this.docShell.contentViewer.authorStyleDisabled = true;
56         break;
57     }
58   }
60   /**
61    * Returns links that would represent stylesheets once loaded.
62    */
63   _collectLinks(document) {
64     let result = [];
65     for (let link of document.querySelectorAll("link")) {
66       if (link.namespaceURI !== "http://www.w3.org/1999/xhtml") {
67         continue;
68       }
69       let isStyleSheet = Array.from(link.relList).some(
70         r => r.toLowerCase() == "stylesheet"
71       );
72       if (!isStyleSheet) {
73         continue;
74       }
75       if (!link.href) {
76         continue;
77       }
78       result.push(link);
79     }
80     return result;
81   }
83   /**
84    * Switch the stylesheet so that only the sheet with the given title is enabled.
85    */
86   _switchStylesheet(title) {
87     let document = this.document;
88     let docStyleSheets = Array.from(document.styleSheets);
89     let links;
91     // Does this doc contain a stylesheet with this title?
92     // If not, it's a subframe's stylesheet that's being changed,
93     // so no need to disable stylesheets here.
94     let docContainsStyleSheet = !title;
95     if (title) {
96       links = this._collectLinks(document);
97       docContainsStyleSheet =
98         docStyleSheets.some(sheet => sheet.title == title) ||
99         links.some(link => link.title == title);
100     }
102     for (let sheet of docStyleSheets) {
103       if (sheet.title) {
104         if (docContainsStyleSheet) {
105           sheet.disabled = sheet.title !== title;
106         }
107       } else if (sheet.disabled) {
108         sheet.disabled = false;
109       }
110     }
112     // If there's no title, we just need to disable potentially-enabled
113     // stylesheets via document.styleSheets, so no need to deal with links
114     // there.
115     //
116     // We don't want to enable <link rel="stylesheet" disabled> without title
117     // that were not enabled before.
118     if (title) {
119       for (let link of links) {
120         if (link.title == title && link.disabled) {
121           link.disabled = false;
122         }
123       }
124     }
125   }
127   #collectAndSendSheets() {
128     let window = this.browsingContext.associatedWindow;
129     window.requestIdleCallback(() => {
130       if (!window || window.closed) {
131         return;
132       }
133       let filteredStyleSheets = this.#collectStyleSheets(window);
134       this.sendAsyncMessage("PageStyle:Add", {
135         filteredStyleSheets,
136         preferredStyleSheetSet: this.document.preferredStyleSheetSet,
137       });
138     });
139   }
141   /**
142    * Get the stylesheets that have a title (and thus can be switched) in this
143    * webpage.
144    *
145    * @param content     The window object for the page.
146    */
147   #collectStyleSheets(content) {
148     let result = [];
149     let document = content.document;
151     for (let sheet of document.styleSheets) {
152       let title = sheet.title;
153       if (!title) {
154         // Sheets without a title are not alternates.
155         continue;
156       }
158       // Skip any stylesheets that don't match the screen media type.
159       let media = sheet.media.mediaText;
160       if (media && !content.matchMedia(media).matches) {
161         continue;
162       }
164       // We skip links here, see below.
165       if (
166         sheet.href &&
167         sheet.ownerNode &&
168         sheet.ownerNode.nodeName.toLowerCase() == "link"
169       ) {
170         continue;
171       }
173       let disabled = sheet.disabled;
174       result.push({ title, disabled });
175     }
177     // This is tricky, because we can't just rely on document.styleSheets, as
178     // `<link disabled>` makes the sheet don't appear there at all.
179     for (let link of this._collectLinks(document)) {
180       let title = link.title;
181       if (!title) {
182         continue;
183       }
185       let media = link.media;
186       if (media && !content.matchMedia(media).matches) {
187         continue;
188       }
190       let disabled =
191         link.disabled ||
192         !!link.sheet?.disabled ||
193         document.preferredStyleSheetSet != title;
194       result.push({ title, disabled });
195     }
197     return result;
198   }