Source code

Revision control

Other Tools

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/. */
4
"use strict";
5
6
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
7
8
var EXPORTED_SYMBOLS = ["PageStyleChild"];
9
10
class PageStyleChild extends JSWindowActorChild {
11
handleEvent(event) {
12
// On page show, tell the parent all of the stylesheets this document has.
13
if (event.type == "pageshow") {
14
// If we are in the topmost browsing context,
15
// delete the stylesheets from the previous page.
16
if (this.browsingContext.top === this.browsingContext) {
17
this.sendAsyncMessage("PageStyle:Clear");
18
}
19
20
let window = event.target.ownerGlobal;
21
window.requestIdleCallback(() => {
22
if (!window || window.closed) {
23
return;
24
}
25
let styleSheets = Array.from(this.document.styleSheets);
26
let filteredStyleSheets = this._filterStyleSheets(styleSheets, window);
27
28
this.sendAsyncMessage("PageStyle:Add", {
29
filteredStyleSheets,
30
authorStyleDisabled: this.docShell.contentViewer.authorStyleDisabled,
31
preferredStyleSheetSet: this.document.preferredStyleSheetSet,
32
});
33
});
34
}
35
}
36
37
receiveMessage(msg) {
38
switch (msg.name) {
39
// Sent when the page's enabled style sheet is changed.
40
case "PageStyle:Switch":
41
this.docShell.contentViewer.authorStyleDisabled = false;
42
this._switchStylesheet(msg.data.title);
43
break;
44
// Sent when "No Style" is chosen.
45
case "PageStyle:Disable":
46
this.docShell.contentViewer.authorStyleDisabled = true;
47
break;
48
}
49
}
50
51
/**
52
* Switch the stylesheet so that only the sheet with the given title is enabled.
53
*/
54
_switchStylesheet(title) {
55
let docStyleSheets = this.document.styleSheets;
56
57
// Does this doc contain a stylesheet with this title?
58
// If not, it's a subframe's stylesheet that's being changed,
59
// so no need to disable stylesheets here.
60
let docContainsStyleSheet = false;
61
for (let docStyleSheet of docStyleSheets) {
62
if (docStyleSheet.title === title) {
63
docContainsStyleSheet = true;
64
break;
65
}
66
}
67
68
for (let docStyleSheet of docStyleSheets) {
69
if (docStyleSheet.title) {
70
if (docContainsStyleSheet) {
71
docStyleSheet.disabled = docStyleSheet.title !== title;
72
}
73
} else if (docStyleSheet.disabled) {
74
docStyleSheet.disabled = false;
75
}
76
}
77
}
78
79
/**
80
* Filter the stylesheets that actually apply to this webpage.
81
* @param styleSheets The list of stylesheets from the document.
82
* @param content The window object that the webpage lives in.
83
*/
84
_filterStyleSheets(styleSheets, content) {
85
let result = [];
86
87
// Only stylesheets with a title can act as an alternative stylesheet.
88
for (let currentStyleSheet of styleSheets) {
89
if (!currentStyleSheet.title) {
90
continue;
91
}
92
93
// Skip any stylesheets that don't match the screen media type.
94
if (currentStyleSheet.media.length) {
95
let mediaQueryList = currentStyleSheet.media.mediaText;
96
if (!content.matchMedia(mediaQueryList).matches) {
97
continue;
98
}
99
}
100
101
let URI;
102
try {
103
if (
104
!currentStyleSheet.ownerNode ||
105
// Special-case style nodes, which have no href.
106
currentStyleSheet.ownerNode.nodeName.toLowerCase() != "style"
107
) {
108
URI = Services.io.newURI(currentStyleSheet.href);
109
}
110
} catch (e) {
111
if (e.result != Cr.NS_ERROR_MALFORMED_URI) {
112
throw e;
113
}
114
continue;
115
}
116
117
// We won't send data URIs all of the way up to the parent, as these
118
// can be arbitrarily large.
119
let sentURI = !URI || URI.scheme == "data" ? null : URI.spec;
120
121
result.push({
122
title: currentStyleSheet.title,
123
disabled: currentStyleSheet.disabled,
124
href: sentURI,
125
});
126
}
127
128
return result;
129
}
130
}