1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
/* 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";
const InspectorUtils = require("InspectorUtils");
loader.lazyRequireGetter(
this,
"loadSheet",
"devtools/shared/layout/utils",
true
);
loader.lazyRequireGetter(
this,
"removeSheet",
"devtools/shared/layout/utils",
true
);
// How many text runs are we highlighting at a time. There may be many text runs, and we
// want to prevent performance problems.
const MAX_TEXT_RANGES = 100;
// This stylesheet is inserted into the page to customize the color of the selected text
// runs.
// Note that this color is defined as --highlighter-content-color in the highlighters.css
// file, and corresponds to the box-model content color. We want to give it an opacity of
// 0.6 here.
const STYLESHEET_URI =
"data:text/css," +
encodeURIComponent(
"::selection{background-color:hsl(197,71%,73%,.6)!important;}"
);
/**
* This highlighter highlights runs of text in the page that have been rendered given a
* certain font. The highlighting is done with window selection ranges, so no extra
* markup is being inserted into the content page.
*/
class FontsHighlighter {
constructor(highlighterEnv) {
this.env = highlighterEnv;
}
destroy() {
this.hide();
this.env = this.currentNode = null;
}
get currentNodeDocument() {
if (!this.currentNode) {
return this.env.document;
}
if (this.currentNode.nodeType === this.currentNode.DOCUMENT_NODE) {
return this.currentNode;
}
return this.currentNode.ownerDocument;
}
/**
* Show the highlighter for a given node.
* @param {DOMNode} node The node in which we want to search for text runs.
* @param {Object} options A bunch of options that can be set:
* - {String} name The actual font name to look for in the node.
* - {String} CSSFamilyName The CSS font-family name given to this font.
*/
show(node, options) {
this.currentNode = node;
const doc = this.currentNodeDocument;
// Get all of the fonts used to render content inside the node.
const searchRange = doc.createRange();
searchRange.selectNodeContents(node);
const fonts = InspectorUtils.getUsedFontFaces(searchRange, MAX_TEXT_RANGES);
// Find the ones we want, based on the provided option.
const matchingFonts = fonts.filter(
f => f.CSSFamilyName === options.CSSFamilyName && f.name === options.name
);
if (!matchingFonts.length) {
return;
}
// Load the stylesheet that will customize the color of the highlighter (using a
// ::selection rule).
loadSheet(this.env.window, STYLESHEET_URI);
// Create a multi-selection in the page to highlight the text runs.
const selection = doc.defaultView.getSelection();
selection.removeAllRanges();
for (const matchingFont of matchingFonts) {
for (const range of matchingFont.ranges) {
selection.addRange(range);
}
}
}
hide() {
// No node was highlighted before, don't need to continue any further.
if (!this.currentNode) {
return;
}
try {
removeSheet(this.env.window, STYLESHEET_URI);
} catch (e) {
// Silently fail here as we might not have inserted the stylesheet at all.
}
// Simply remove all current ranges in the seletion.
const doc = this.currentNodeDocument;
const selection = doc.defaultView.getSelection();
selection.removeAllRanges();
}
}
exports.FontsHighlighter = FontsHighlighter;
|