Repository navigation
Expand file tree
/
Copy pathcontent.js
More file actions
156 lines (135 loc) · 4.86 KB
/
Copy pathcontent.js
File metadata and controls
156 lines (135 loc) · 4.86 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* ═══════════════════════════════════════════════════════════════════════════
Fontmaster content.js — Injection & Runtime Engine
═══════════════════════════════════════════════════════════════════════════ */
'use strict';
(function () {
const STYLESHEET_ID = 'fontmaster-stylesheet';
const CUSTOM_CSS_ID = 'fontmaster-custom-css';
/**
* Inject or update the dynamic CSS and font links in the page head.
*/
async function applyFontOverride(data, settings) {
if (!data || !data.family) return;
// 1. Inject or update external stylesheet link (Google Fonts / Bunny Fonts)
if (data.url && data.source !== 'local') {
let link = document.getElementById(STYLESHEET_ID);
if (!link) {
link = document.createElement('link');
link.id = STYLESHEET_ID;
link.rel = 'stylesheet';
(document.head || document.documentElement).appendChild(link);
}
if (link.href !== data.url) {
link.href = data.url;
}
} else {
// If local font or no URL, remove previous remote stylesheet
removeStylesheetLink();
}
// 2. Build and inject main CSS rules override
const cssContent = buildAppliedCSS(data.family, data.config || {}, settings || {});
let style = document.getElementById(CUSTOM_CSS_ID);
if (!style) {
style = document.createElement('style');
style.id = CUSTOM_CSS_ID;
(document.head || document.documentElement).appendChild(style);
}
style.textContent = cssContent;
// 3. Verify font loading status using Document.fonts API
return verifyFontLoaded(data.family);
}
/**
* Verify whether the browser has successfully parsed and rendered the target font.
*/
async function verifyFontLoaded(family) {
if (!('fonts' in document)) {
return { success: true };
}
try {
const cleanFamily = family.replace(/\+/g, ' ');
// Attempt to load the font explicitly within a short timeframe
const loadedFonts = await document.fonts.load(`16px "${cleanFamily}"`);
if (loadedFonts.length === 0) {
return {
success: true,
warning: `Font "${cleanFamily}" could not be confirmed by the browser. A fallback font may be active.`
};
}
return { success: true };
} catch (err) {
return {
success: true,
warning: `Font "${family}" validation bypassed due to page security limits (CSP).`
};
}
}
/**
* Remove external font stylesheet link element.
*/
function removeStylesheetLink() {
const link = document.getElementById(STYLESHEET_ID);
if (link) link.remove();
}
/**
* Remove injected CSS style override node.
*/
function removeCustomCSS() {
const style = document.getElementById(CUSTOM_CSS_ID);
if (style) style.remove();
}
/**
* Remove all extension styles from the target DOM.
*/
function removeFontOverride() {
removeStylesheetLink();
removeCustomCSS();
}
/**
* Initialize state from storage when content script loads.
*/
async function init() {
try {
const currentDomain = normalizeDomain(window.location.href);
if (!currentDomain) return;
const res = await chrome.storage.sync.get(['fontmasterSettings', 'appliedFonts']);
const settings = res.fontmasterSettings || DEFAULT_SETTINGS;
const appliedFonts = res.appliedFonts || {};
const fontData = appliedFonts[currentDomain];
if (fontData && fontData.enabled !== false) {
await applyFontOverride(fontData, settings);
}
} catch (e) {
console.warn('[Fontmaster] Content script init failed:', e);
}
}
/**
* Message Listener for action requests from the popup.
*/
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (!request || !request.action) return false;
switch (request.action) {
case 'applyFont':
applyFontOverride(request.data, request.settings)
.then(result => sendResponse(result || { success: true }))
.catch(err => sendResponse({ success: false, error: err.message }));
return true; // Keep message channel open for async response
case 'removeFont':
removeFontOverride();
sendResponse({ success: true });
break;
case 'settingsUpdated':
init();
sendResponse({ success: true });
break;
default:
break;
}
return false;
});
// Execute initialization when document structure is available
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();