@gxp-dev/tools 2.0.6 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/commands/build.js +18 -12
- package/browser-extensions/README.md +1 -0
- package/browser-extensions/chrome/background.js +857 -0
- package/browser-extensions/chrome/content.js +51 -0
- package/browser-extensions/chrome/devtools.html +9 -0
- package/browser-extensions/chrome/devtools.js +23 -0
- package/browser-extensions/chrome/icons/gx_off_128.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_16.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_32.png +0 -0
- package/browser-extensions/chrome/icons/gx_off_64.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_128.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_16.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_32.png +0 -0
- package/browser-extensions/chrome/icons/gx_on_64.png +0 -0
- package/browser-extensions/chrome/inspector.js +1087 -0
- package/browser-extensions/chrome/manifest.json +70 -0
- package/browser-extensions/chrome/panel.html +638 -0
- package/browser-extensions/chrome/panel.js +862 -0
- package/browser-extensions/chrome/popup.html +399 -0
- package/browser-extensions/chrome/popup.js +515 -0
- package/browser-extensions/chrome/rules.json +1 -0
- package/browser-extensions/chrome/test-chrome.html +145 -0
- package/browser-extensions/chrome/test-mixed-content.html +190 -0
- package/browser-extensions/chrome/test-uri-pattern.html +199 -0
- package/browser-extensions/firefox/README.md +134 -0
- package/browser-extensions/firefox/background.js +804 -0
- package/browser-extensions/firefox/content.js +120 -0
- package/browser-extensions/firefox/debug-errors.html +229 -0
- package/browser-extensions/firefox/debug-https.html +113 -0
- package/browser-extensions/firefox/devtools.html +9 -0
- package/browser-extensions/firefox/devtools.js +24 -0
- package/browser-extensions/firefox/icons/gx_off_128.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_16.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_32.png +0 -0
- package/browser-extensions/firefox/icons/gx_off_64.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_128.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_16.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_32.png +0 -0
- package/browser-extensions/firefox/icons/gx_on_64.png +0 -0
- package/browser-extensions/firefox/inspector.js +1087 -0
- package/browser-extensions/firefox/manifest.json +67 -0
- package/browser-extensions/firefox/panel.html +638 -0
- package/browser-extensions/firefox/panel.js +862 -0
- package/browser-extensions/firefox/popup.html +525 -0
- package/browser-extensions/firefox/popup.js +536 -0
- package/browser-extensions/firefox/test-gramercy.html +126 -0
- package/browser-extensions/firefox/test-imports.html +58 -0
- package/browser-extensions/firefox/test-masking.html +147 -0
- package/browser-extensions/firefox/test-uri-pattern.html +199 -0
- package/package.json +7 -2
- package/runtime/PortalContainer.vue +326 -0
- package/runtime/dev-tools/DevToolsModal.vue +217 -0
- package/runtime/dev-tools/LayoutSwitcher.vue +221 -0
- package/runtime/dev-tools/MockDataEditor.vue +621 -0
- package/runtime/dev-tools/SocketSimulator.vue +562 -0
- package/runtime/dev-tools/StoreInspector.vue +644 -0
- package/runtime/dev-tools/index.js +6 -0
- package/runtime/gxpStringsPlugin.js +428 -0
- package/runtime/index.html +22 -0
- package/runtime/main.js +32 -0
- package/runtime/mock-api/auth-middleware.js +97 -0
- package/runtime/mock-api/image-generator.js +221 -0
- package/runtime/mock-api/index.js +197 -0
- package/runtime/mock-api/response-generator.js +394 -0
- package/runtime/mock-api/route-generator.js +323 -0
- package/runtime/mock-api/socket-triggers.js +371 -0
- package/runtime/mock-api/spec-loader.js +300 -0
- package/runtime/server.js +180 -0
- package/runtime/stores/gxpPortalConfigStore.js +554 -0
- package/runtime/stores/index.js +6 -0
- package/runtime/vite-inspector-plugin.js +749 -0
- package/runtime/vite-source-tracker-plugin.js +232 -0
- package/runtime/vite.config.js +402 -0
- package/scripts/launch-chrome.js +90 -0
- package/scripts/pack-chrome.js +91 -0
- package/socket-events/AiSessionMessageCreated.json +18 -0
- package/socket-events/SocialStreamPostCreated.json +24 -0
- package/socket-events/SocialStreamPostVariantCompleted.json +23 -0
- package/template/README.md +332 -0
- package/template/app-manifest.json +32 -0
- package/template/dev-assets/images/avatar-placeholder.png +0 -0
- package/template/dev-assets/images/background-placeholder.jpg +0 -0
- package/template/dev-assets/images/banner-placeholder.jpg +0 -0
- package/template/dev-assets/images/icon-placeholder.png +0 -0
- package/template/dev-assets/images/logo-placeholder.png +0 -0
- package/template/dev-assets/images/product-placeholder.jpg +0 -0
- package/template/dev-assets/images/thumbnail-placeholder.jpg +0 -0
- package/template/env.example +51 -0
- package/template/gitignore +53 -0
- package/template/index.html +22 -0
- package/template/main.js +28 -0
- package/template/src/DemoPage.vue +459 -0
- package/template/src/Plugin.vue +38 -0
- package/template/src/stores/index.js +9 -0
- package/template/src/stores/test-data.json +173 -0
- package/template/theme-layouts/AdditionalStyling.css +0 -0
- package/template/theme-layouts/PrivateLayout.vue +39 -0
- package/template/theme-layouts/PublicLayout.vue +39 -0
- package/template/theme-layouts/SystemLayout.vue +39 -0
- package/template/vite.config.js +333 -0
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
// Extension state
|
|
2
|
+
let config = {
|
|
3
|
+
enabled: false,
|
|
4
|
+
// Legacy fields for backward compatibility
|
|
5
|
+
redirectUrl: "https://localhost:3060/src/Plugin.vue",
|
|
6
|
+
urlPattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
|
|
7
|
+
useCustomPattern: false,
|
|
8
|
+
// New rules-based configuration
|
|
9
|
+
rules: {
|
|
10
|
+
js: {
|
|
11
|
+
enabled: true,
|
|
12
|
+
pattern: "uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
|
|
13
|
+
redirectUrl: "https://localhost:3060/src/Plugin.vue",
|
|
14
|
+
useCustomPattern: false,
|
|
15
|
+
},
|
|
16
|
+
css: {
|
|
17
|
+
enabled: false,
|
|
18
|
+
pattern:
|
|
19
|
+
"uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
|
|
20
|
+
redirectUrl: "",
|
|
21
|
+
returnBlank: false,
|
|
22
|
+
useCustomPattern: false,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
maskingMode: false,
|
|
26
|
+
clearCacheOnEnable: true,
|
|
27
|
+
disableCacheForRedirects: true,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let notificationTimeouts = new Map();
|
|
31
|
+
let cacheBlacklist = new Set();
|
|
32
|
+
|
|
33
|
+
// Initialize extension
|
|
34
|
+
browser.runtime.onStartup.addListener(initializeExtension);
|
|
35
|
+
browser.runtime.onInstalled.addListener(initializeExtension);
|
|
36
|
+
|
|
37
|
+
async function initializeExtension() {
|
|
38
|
+
console.log("[JavaScript Proxy] Initializing extension...");
|
|
39
|
+
try {
|
|
40
|
+
// Load saved configuration
|
|
41
|
+
const defaultConfig = {
|
|
42
|
+
enabled: false,
|
|
43
|
+
// Legacy fields for backward compatibility
|
|
44
|
+
redirectUrl: "https://localhost:3060/src/Plugin.vue",
|
|
45
|
+
urlPattern:
|
|
46
|
+
"uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
|
|
47
|
+
useCustomPattern: false,
|
|
48
|
+
// New rules-based configuration
|
|
49
|
+
rules: {
|
|
50
|
+
js: {
|
|
51
|
+
enabled: true,
|
|
52
|
+
pattern:
|
|
53
|
+
"uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
|
|
54
|
+
redirectUrl: "https://localhost:3060/src/Plugin.vue",
|
|
55
|
+
useCustomPattern: false,
|
|
56
|
+
},
|
|
57
|
+
css: {
|
|
58
|
+
enabled: false,
|
|
59
|
+
pattern:
|
|
60
|
+
"uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
|
|
61
|
+
redirectUrl: "",
|
|
62
|
+
returnBlank: false,
|
|
63
|
+
useCustomPattern: false,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
maskingMode: false,
|
|
67
|
+
clearCacheOnEnable: true,
|
|
68
|
+
disableCacheForRedirects: true,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const result = await browser.storage.sync.get(defaultConfig);
|
|
72
|
+
config = result;
|
|
73
|
+
|
|
74
|
+
// Migrate legacy configuration to new rules format
|
|
75
|
+
config = migrateConfig(config);
|
|
76
|
+
|
|
77
|
+
console.log("[JavaScript Proxy] Loaded configuration:", config);
|
|
78
|
+
|
|
79
|
+
updateIcon();
|
|
80
|
+
updateRequestListener();
|
|
81
|
+
|
|
82
|
+
console.log("[JavaScript Proxy] Extension initialized successfully");
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error("[JavaScript Proxy] Failed to initialize:", error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Migrate legacy configuration to new rules format
|
|
89
|
+
function migrateConfig(config) {
|
|
90
|
+
// If rules don't exist, create them from legacy config
|
|
91
|
+
if (!config.rules) {
|
|
92
|
+
config.rules = {
|
|
93
|
+
js: {
|
|
94
|
+
enabled: true,
|
|
95
|
+
pattern:
|
|
96
|
+
config.urlPattern ||
|
|
97
|
+
"uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
|
|
98
|
+
redirectUrl:
|
|
99
|
+
config.redirectUrl || "https://localhost:3060/src/Plugin.vue",
|
|
100
|
+
useCustomPattern: config.useCustomPattern || false,
|
|
101
|
+
},
|
|
102
|
+
css: {
|
|
103
|
+
enabled: true,
|
|
104
|
+
pattern:
|
|
105
|
+
"uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
|
|
106
|
+
redirectUrl: "",
|
|
107
|
+
returnBlank: true,
|
|
108
|
+
useCustomPattern: false,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
} else {
|
|
112
|
+
// Ensure all required fields exist
|
|
113
|
+
if (!config.rules.js) {
|
|
114
|
+
config.rules.js = {
|
|
115
|
+
enabled: true,
|
|
116
|
+
pattern:
|
|
117
|
+
config.urlPattern ||
|
|
118
|
+
"uploads\\/plugin-version\\/\\d+\\/file_name\\/.*\\.js(\\?.*)?",
|
|
119
|
+
redirectUrl:
|
|
120
|
+
config.redirectUrl || "https://localhost:3060/src/Plugin.vue",
|
|
121
|
+
useCustomPattern: config.useCustomPattern || false,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (!config.rules.css) {
|
|
125
|
+
config.rules.css = {
|
|
126
|
+
enabled: true,
|
|
127
|
+
pattern:
|
|
128
|
+
"uploads\\/plugin-version\\/\\d+\\/style_file_name\\/.*\\.css(\\?.*)?",
|
|
129
|
+
redirectUrl: "",
|
|
130
|
+
returnBlank: true,
|
|
131
|
+
useCustomPattern: false,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return config;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Update toolbar icon based on state
|
|
140
|
+
function updateIcon() {
|
|
141
|
+
const iconPath = config.enabled ? "icons/gx_on" : "icons/gx_off";
|
|
142
|
+
browser.browserAction.setIcon({
|
|
143
|
+
path: {
|
|
144
|
+
16: `${iconPath}_16.png`,
|
|
145
|
+
32: `${iconPath}_32.png`,
|
|
146
|
+
48: `${iconPath}_48.png`,
|
|
147
|
+
128: `${iconPath}_128.png`,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const title = config.enabled
|
|
152
|
+
? "JavaScript Proxy (ON)"
|
|
153
|
+
: "JavaScript Proxy (OFF)";
|
|
154
|
+
browser.browserAction.setTitle({ title });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Cache management functions
|
|
158
|
+
async function clearCacheForPattern(urlPattern = null) {
|
|
159
|
+
try {
|
|
160
|
+
// Collect all patterns that need cache clearing
|
|
161
|
+
const patterns = [];
|
|
162
|
+
|
|
163
|
+
if (urlPattern) {
|
|
164
|
+
patterns.push(urlPattern);
|
|
165
|
+
} else {
|
|
166
|
+
// Clear cache for all enabled rules
|
|
167
|
+
for (const [ruleType, rule] of Object.entries(config.rules || {})) {
|
|
168
|
+
if (rule.enabled && rule.pattern) {
|
|
169
|
+
patterns.push(rule.pattern);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Include legacy pattern if exists
|
|
174
|
+
if (config.urlPattern) {
|
|
175
|
+
patterns.push(config.urlPattern);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(`[JavaScript Proxy] Clearing cache for patterns:`, patterns);
|
|
180
|
+
|
|
181
|
+
// Clear browser cache
|
|
182
|
+
await browser.browsingData.removeCache({
|
|
183
|
+
origins: [], // Empty array means all origins
|
|
184
|
+
since: 0, // Clear all cache entries
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Clear service worker caches for each pattern
|
|
188
|
+
for (const pattern of patterns) {
|
|
189
|
+
await clearServiceWorkerCaches(pattern);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log("[JavaScript Proxy] Cache cleared successfully");
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error("[JavaScript Proxy] Error clearing cache:", error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function clearServiceWorkerCaches(urlPattern) {
|
|
199
|
+
try {
|
|
200
|
+
// Get all active tabs
|
|
201
|
+
const tabs = await browser.tabs.query({});
|
|
202
|
+
|
|
203
|
+
// Script to clear service worker caches for matching URLs
|
|
204
|
+
const clearCacheScript = `
|
|
205
|
+
(async function() {
|
|
206
|
+
try {
|
|
207
|
+
if ('caches' in window) {
|
|
208
|
+
const cacheNames = await caches.keys();
|
|
209
|
+
const pattern = new RegExp("${urlPattern.replace(/\\/g, "\\\\")}", "i");
|
|
210
|
+
|
|
211
|
+
for (const cacheName of cacheNames) {
|
|
212
|
+
const cache = await caches.open(cacheName);
|
|
213
|
+
const requests = await cache.keys();
|
|
214
|
+
|
|
215
|
+
for (const request of requests) {
|
|
216
|
+
if (pattern.test(request.url)) {
|
|
217
|
+
await cache.delete(request);
|
|
218
|
+
console.log('[JavaScript Proxy] Deleted from cache:', request.url);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Also try to update service worker registration
|
|
224
|
+
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
|
|
225
|
+
navigator.serviceWorker.controller.postMessage({
|
|
226
|
+
type: 'CLEAR_CACHE_FOR_PATTERN',
|
|
227
|
+
pattern: "${urlPattern.replace(/\\/g, "\\\\")}"
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error('[JavaScript Proxy] Error clearing service worker cache:', error);
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
// Inject the script into each tab
|
|
238
|
+
for (const tab of tabs) {
|
|
239
|
+
try {
|
|
240
|
+
if (
|
|
241
|
+
tab.url &&
|
|
242
|
+
!tab.url.startsWith("moz-extension://") &&
|
|
243
|
+
!tab.url.startsWith("about:")
|
|
244
|
+
) {
|
|
245
|
+
await browser.tabs.executeScript(tab.id, {
|
|
246
|
+
code: clearCacheScript,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
// Tab might not allow script injection, continue with others
|
|
251
|
+
console.debug(
|
|
252
|
+
`[JavaScript Proxy] Could not inject cache clear script into tab ${tab.id}:`,
|
|
253
|
+
error
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
console.log("[JavaScript Proxy] Service worker cache clearing attempted");
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error(
|
|
261
|
+
"[JavaScript Proxy] Error clearing service worker caches:",
|
|
262
|
+
error
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function addToCacheBlacklist(url) {
|
|
268
|
+
try {
|
|
269
|
+
const urlObj = new URL(url);
|
|
270
|
+
const origin = urlObj.origin;
|
|
271
|
+
cacheBlacklist.add(origin);
|
|
272
|
+
console.log(`[JavaScript Proxy] Added ${origin} to cache blacklist`);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error("[JavaScript Proxy] Error adding to cache blacklist:", error);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function shouldDisableCache(url) {
|
|
279
|
+
if (!config.disableCacheForRedirects) return false;
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
// Check against all enabled rules
|
|
283
|
+
for (const [ruleType, rule] of Object.entries(config.rules || {})) {
|
|
284
|
+
if (rule.enabled) {
|
|
285
|
+
const pattern =
|
|
286
|
+
rule.useCustomPattern && rule.pattern ? rule.pattern : rule.pattern;
|
|
287
|
+
const regex = new RegExp(pattern, "i");
|
|
288
|
+
if (regex.test(url)) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Fallback to legacy pattern for backward compatibility
|
|
295
|
+
if (config.urlPattern) {
|
|
296
|
+
const regex = new RegExp(config.urlPattern, "i");
|
|
297
|
+
return regex.test(url);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return false;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error(
|
|
303
|
+
"[JavaScript Proxy] Error checking cache disable pattern:",
|
|
304
|
+
error
|
|
305
|
+
);
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Handle web requests
|
|
311
|
+
function handleRequest(details) {
|
|
312
|
+
if (!config.enabled) return {};
|
|
313
|
+
|
|
314
|
+
// Skip certain request types that commonly cause issues
|
|
315
|
+
const skipTypes = ["ping", "csp_report", "beacon"];
|
|
316
|
+
if (skipTypes.includes(details.type)) {
|
|
317
|
+
return {};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Skip data URLs and blob URLs
|
|
321
|
+
if (
|
|
322
|
+
details.url.startsWith("data:") ||
|
|
323
|
+
details.url.startsWith("blob:") ||
|
|
324
|
+
details.url.startsWith("chrome:") ||
|
|
325
|
+
details.url.startsWith("moz-extension:")
|
|
326
|
+
) {
|
|
327
|
+
return {};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
// Check each rule type
|
|
332
|
+
for (const [ruleType, rule] of Object.entries(config.rules || {})) {
|
|
333
|
+
if (!rule.enabled || !rule.pattern) continue;
|
|
334
|
+
|
|
335
|
+
// Check if this rule type matches the resource type
|
|
336
|
+
if (ruleType === "js" && details.type !== "script") continue;
|
|
337
|
+
if (ruleType === "css" && details.type !== "stylesheet") continue;
|
|
338
|
+
|
|
339
|
+
const regex = new RegExp(rule.pattern, "i");
|
|
340
|
+
|
|
341
|
+
// Test the full URL against the pattern
|
|
342
|
+
if (regex.test(details.url)) {
|
|
343
|
+
// Handle blank return for CSS
|
|
344
|
+
if (rule.returnBlank) {
|
|
345
|
+
console.log(
|
|
346
|
+
`[JavaScript Proxy] Returning blank for ${ruleType}: ${details.url}`
|
|
347
|
+
);
|
|
348
|
+
// Return a data URL with empty content
|
|
349
|
+
const blankUrl =
|
|
350
|
+
ruleType === "css"
|
|
351
|
+
? "data:text/css;charset=utf-8,"
|
|
352
|
+
: "data:text/javascript;charset=utf-8,";
|
|
353
|
+
return { redirectUrl: blankUrl };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (rule.redirectUrl) {
|
|
357
|
+
let newUrl = rule.redirectUrl;
|
|
358
|
+
|
|
359
|
+
// Ensure URL has protocol
|
|
360
|
+
if (!newUrl.includes("://")) {
|
|
361
|
+
newUrl = "https://" + newUrl;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const requestType = getRequestTypeDescription(details.type);
|
|
365
|
+
|
|
366
|
+
// Check if this would create a redirect loop
|
|
367
|
+
if (details.url === newUrl || details.url.includes("localhost")) {
|
|
368
|
+
console.log(
|
|
369
|
+
`[JavaScript Proxy] Skipping redirect loop: ${details.url}`
|
|
370
|
+
);
|
|
371
|
+
return {};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check if this rule should use URL masking
|
|
375
|
+
if (config.maskingMode) {
|
|
376
|
+
// Store original URL for header modification
|
|
377
|
+
storeOriginalUrl(details.requestId, details.url, newUrl);
|
|
378
|
+
|
|
379
|
+
console.log(
|
|
380
|
+
`[JavaScript Proxy] Masking ${ruleType}: ${details.url} (routing to ${newUrl})`
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Don't redirect - we'll modify headers instead
|
|
384
|
+
return {};
|
|
385
|
+
} else {
|
|
386
|
+
// Clear cache if enabled (for traditional redirect)
|
|
387
|
+
if (config.clearCacheOnEnable) {
|
|
388
|
+
clearCacheForPattern(rule.pattern);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Traditional redirect approach
|
|
392
|
+
showProxyNotification(details.url, newUrl, requestType);
|
|
393
|
+
|
|
394
|
+
console.log(
|
|
395
|
+
`[JavaScript Proxy] Redirecting ${ruleType}: ${details.url} → ${newUrl}`
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
return { redirectUrl: newUrl };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Fallback to legacy pattern for backward compatibility
|
|
405
|
+
if (config.urlPattern && details.type === "script") {
|
|
406
|
+
const regex = new RegExp(config.urlPattern, "i");
|
|
407
|
+
|
|
408
|
+
if (regex.test(details.url)) {
|
|
409
|
+
let newUrl = config.redirectUrl;
|
|
410
|
+
|
|
411
|
+
if (!newUrl.includes("://")) {
|
|
412
|
+
newUrl = "https://" + newUrl;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const requestType = getRequestTypeDescription(details.type);
|
|
416
|
+
|
|
417
|
+
if (details.url === newUrl || details.url.includes("localhost")) {
|
|
418
|
+
console.log(
|
|
419
|
+
`[JavaScript Proxy] Skipping redirect loop: ${details.url}`
|
|
420
|
+
);
|
|
421
|
+
return {};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (config.maskingMode) {
|
|
425
|
+
storeOriginalUrl(details.requestId, details.url, newUrl);
|
|
426
|
+
console.log(
|
|
427
|
+
`[JavaScript Proxy] Legacy masking: ${details.url} (routing to ${newUrl})`
|
|
428
|
+
);
|
|
429
|
+
return {};
|
|
430
|
+
} else {
|
|
431
|
+
if (config.clearCacheOnEnable) {
|
|
432
|
+
clearCacheForPattern(config.urlPattern);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
showProxyNotification(details.url, newUrl, requestType);
|
|
436
|
+
console.log(
|
|
437
|
+
`[JavaScript Proxy] Legacy redirecting: ${details.url} → ${newUrl}`
|
|
438
|
+
);
|
|
439
|
+
return { redirectUrl: newUrl };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error("[JavaScript Proxy] Error processing request:", error);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return {};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Store mapping of request IDs to proxy info for header modification
|
|
451
|
+
const pendingProxyRequests = new Map();
|
|
452
|
+
|
|
453
|
+
function storeOriginalUrl(requestId, originalUrl, redirectUrl) {
|
|
454
|
+
pendingProxyRequests.set(requestId, {
|
|
455
|
+
originalUrl,
|
|
456
|
+
redirectUrl,
|
|
457
|
+
timestamp: Date.now(),
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Clean up old entries after 30 seconds
|
|
461
|
+
setTimeout(() => {
|
|
462
|
+
pendingProxyRequests.delete(requestId);
|
|
463
|
+
}, 30000);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Get human-readable description of request type
|
|
467
|
+
function getRequestTypeDescription(type) {
|
|
468
|
+
const typeMap = {
|
|
469
|
+
script: "JavaScript",
|
|
470
|
+
main_frame: "Page",
|
|
471
|
+
sub_frame: "Frame",
|
|
472
|
+
stylesheet: "CSS",
|
|
473
|
+
image: "Image",
|
|
474
|
+
font: "Font",
|
|
475
|
+
object: "Plugin",
|
|
476
|
+
xmlhttprequest: "XHR",
|
|
477
|
+
ping: "Ping",
|
|
478
|
+
csp_report: "CSP Report",
|
|
479
|
+
media: "Media",
|
|
480
|
+
websocket: "WebSocket",
|
|
481
|
+
other: "Other",
|
|
482
|
+
};
|
|
483
|
+
return typeMap[type] || type;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Show desktop notification for proxy activity
|
|
487
|
+
function showProxyNotification(from, to, requestType = "") {
|
|
488
|
+
const shortFrom = from.length > 50 ? from.substring(0, 47) + "..." : from;
|
|
489
|
+
const shortTo = to.length > 30 ? to.substring(0, 27) + "..." : to;
|
|
490
|
+
|
|
491
|
+
const notificationId = `proxy-${Date.now()}`;
|
|
492
|
+
|
|
493
|
+
browser.notifications.create(notificationId, {
|
|
494
|
+
type: "basic",
|
|
495
|
+
iconUrl: "icons/gx_on_48.png",
|
|
496
|
+
title: "JavaScript Proxy Active",
|
|
497
|
+
message: `${requestType}\n${shortFrom}\n→ ${shortTo}`,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Clear existing timeout for this type
|
|
501
|
+
if (notificationTimeouts.has("proxy")) {
|
|
502
|
+
clearTimeout(notificationTimeouts.get("proxy"));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Auto-dismiss after 3 seconds
|
|
506
|
+
const timeoutId = setTimeout(() => {
|
|
507
|
+
browser.notifications.clear(notificationId);
|
|
508
|
+
notificationTimeouts.delete("proxy");
|
|
509
|
+
}, 3000);
|
|
510
|
+
|
|
511
|
+
notificationTimeouts.set("proxy", timeoutId);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Handle request headers for masking mode and cache control
|
|
515
|
+
function handleRequestHeaders(details) {
|
|
516
|
+
if (!config.enabled) return {};
|
|
517
|
+
|
|
518
|
+
// Initialize modifications
|
|
519
|
+
const modifications = { requestHeaders: details.requestHeaders };
|
|
520
|
+
let hasModifications = false;
|
|
521
|
+
|
|
522
|
+
// Handle masking mode
|
|
523
|
+
if (config.maskingMode) {
|
|
524
|
+
const proxyInfo = pendingProxyRequests.get(details.requestId);
|
|
525
|
+
if (proxyInfo) {
|
|
526
|
+
// Add/modify headers to properly route to localhost
|
|
527
|
+
const hostHeader = modifications.requestHeaders.find(
|
|
528
|
+
(h) => h.name.toLowerCase() === "host"
|
|
529
|
+
);
|
|
530
|
+
if (hostHeader) {
|
|
531
|
+
try {
|
|
532
|
+
const proxyUrl = new URL(proxyInfo.redirectUrl);
|
|
533
|
+
hostHeader.value = proxyUrl.host;
|
|
534
|
+
hasModifications = true;
|
|
535
|
+
} catch (error) {
|
|
536
|
+
console.error("[JavaScript Proxy] Error parsing proxy URL:", error);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Handle cache control for URLs matching our pattern
|
|
543
|
+
if (config.disableCacheForRedirects && shouldDisableCache(details.url)) {
|
|
544
|
+
// Add cache-busting headers
|
|
545
|
+
const cacheHeaders = [
|
|
546
|
+
{ name: "Cache-Control", value: "no-cache, no-store, must-revalidate" },
|
|
547
|
+
{ name: "Pragma", value: "no-cache" },
|
|
548
|
+
{ name: "Expires", value: "0" },
|
|
549
|
+
];
|
|
550
|
+
|
|
551
|
+
cacheHeaders.forEach((cacheHeader) => {
|
|
552
|
+
const existingHeader = modifications.requestHeaders.find(
|
|
553
|
+
(h) => h.name.toLowerCase() === cacheHeader.name.toLowerCase()
|
|
554
|
+
);
|
|
555
|
+
if (existingHeader) {
|
|
556
|
+
existingHeader.value = cacheHeader.value;
|
|
557
|
+
} else {
|
|
558
|
+
modifications.requestHeaders.push(cacheHeader);
|
|
559
|
+
}
|
|
560
|
+
hasModifications = true;
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
console.log(
|
|
564
|
+
`[JavaScript Proxy] Added cache-busting headers for: ${details.url}`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return hasModifications ? modifications : {};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Handle response headers for masking mode and cache control
|
|
572
|
+
function handleResponseHeaders(details) {
|
|
573
|
+
if (!config.enabled) return {};
|
|
574
|
+
|
|
575
|
+
// Initialize modifications
|
|
576
|
+
const modifications = { responseHeaders: details.responseHeaders };
|
|
577
|
+
let hasModifications = false;
|
|
578
|
+
|
|
579
|
+
// Handle masking mode
|
|
580
|
+
if (config.maskingMode) {
|
|
581
|
+
const proxyInfo = pendingProxyRequests.get(details.requestId);
|
|
582
|
+
if (proxyInfo) {
|
|
583
|
+
// Add CORS headers if needed
|
|
584
|
+
const corsHeaders = [
|
|
585
|
+
{ name: "Access-Control-Allow-Origin", value: "*" },
|
|
586
|
+
{
|
|
587
|
+
name: "Access-Control-Allow-Methods",
|
|
588
|
+
value: "GET, POST, PUT, DELETE, OPTIONS",
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: "Access-Control-Allow-Headers",
|
|
592
|
+
value: "Content-Type, Authorization",
|
|
593
|
+
},
|
|
594
|
+
];
|
|
595
|
+
|
|
596
|
+
corsHeaders.forEach((corsHeader) => {
|
|
597
|
+
const existingHeader = modifications.responseHeaders.find(
|
|
598
|
+
(h) => h.name.toLowerCase() === corsHeader.name.toLowerCase()
|
|
599
|
+
);
|
|
600
|
+
if (existingHeader) {
|
|
601
|
+
existingHeader.value = corsHeader.value;
|
|
602
|
+
} else {
|
|
603
|
+
modifications.responseHeaders.push(corsHeader);
|
|
604
|
+
}
|
|
605
|
+
hasModifications = true;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Handle cache control for URLs matching our pattern
|
|
611
|
+
if (config.disableCacheForRedirects && shouldDisableCache(details.url)) {
|
|
612
|
+
// Add response cache-busting headers
|
|
613
|
+
const cacheHeaders = [
|
|
614
|
+
{ name: "Cache-Control", value: "no-cache, no-store, must-revalidate" },
|
|
615
|
+
{ name: "Pragma", value: "no-cache" },
|
|
616
|
+
{ name: "Expires", value: "0" },
|
|
617
|
+
{ name: "Last-Modified", value: new Date().toUTCString() },
|
|
618
|
+
{ name: "ETag", value: `"${Date.now()}"` },
|
|
619
|
+
];
|
|
620
|
+
|
|
621
|
+
cacheHeaders.forEach((cacheHeader) => {
|
|
622
|
+
const existingHeader = modifications.responseHeaders.find(
|
|
623
|
+
(h) => h.name.toLowerCase() === cacheHeader.name.toLowerCase()
|
|
624
|
+
);
|
|
625
|
+
if (existingHeader) {
|
|
626
|
+
existingHeader.value = cacheHeader.value;
|
|
627
|
+
} else {
|
|
628
|
+
modifications.responseHeaders.push(cacheHeader);
|
|
629
|
+
}
|
|
630
|
+
hasModifications = true;
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
console.log(
|
|
634
|
+
`[JavaScript Proxy] Added response cache-busting headers for: ${details.url}`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (hasModifications) {
|
|
639
|
+
console.log(
|
|
640
|
+
`[JavaScript Proxy] Modified response headers for: ${details.url}`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
return hasModifications ? modifications : {};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Update request listener based on current state
|
|
648
|
+
function updateRequestListener() {
|
|
649
|
+
// Remove existing listeners
|
|
650
|
+
if (browser.webRequest.onBeforeRequest.hasListener(handleRequest)) {
|
|
651
|
+
browser.webRequest.onBeforeRequest.removeListener(handleRequest);
|
|
652
|
+
}
|
|
653
|
+
if (
|
|
654
|
+
browser.webRequest.onBeforeSendHeaders.hasListener(handleRequestHeaders)
|
|
655
|
+
) {
|
|
656
|
+
browser.webRequest.onBeforeSendHeaders.removeListener(handleRequestHeaders);
|
|
657
|
+
}
|
|
658
|
+
if (browser.webRequest.onHeadersReceived.hasListener(handleResponseHeaders)) {
|
|
659
|
+
browser.webRequest.onHeadersReceived.removeListener(handleResponseHeaders);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (config.enabled) {
|
|
663
|
+
// Add request interceptor
|
|
664
|
+
browser.webRequest.onBeforeRequest.addListener(
|
|
665
|
+
handleRequest,
|
|
666
|
+
{ urls: ["<all_urls>"] },
|
|
667
|
+
["blocking"]
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
// Add header modification listeners for masking mode OR cache control
|
|
671
|
+
if (config.maskingMode || config.disableCacheForRedirects) {
|
|
672
|
+
browser.webRequest.onBeforeSendHeaders.addListener(
|
|
673
|
+
handleRequestHeaders,
|
|
674
|
+
{ urls: ["<all_urls>"] },
|
|
675
|
+
["blocking", "requestHeaders"]
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
browser.webRequest.onHeadersReceived.addListener(
|
|
679
|
+
handleResponseHeaders,
|
|
680
|
+
{ urls: ["<all_urls>"] },
|
|
681
|
+
["blocking", "responseHeaders"]
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
console.log("[JavaScript Proxy] Request listener enabled");
|
|
686
|
+
} else {
|
|
687
|
+
console.log("[JavaScript Proxy] Request listener disabled");
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ============================================================
|
|
692
|
+
// DevTools Panel Communication
|
|
693
|
+
// ============================================================
|
|
694
|
+
|
|
695
|
+
const devtoolsConnections = new Map();
|
|
696
|
+
|
|
697
|
+
browser.runtime.onConnect.addListener((port) => {
|
|
698
|
+
if (port.name === 'gxp-devtools-panel') {
|
|
699
|
+
const extensionListener = (message, port) => {
|
|
700
|
+
if (message.name === 'init') {
|
|
701
|
+
devtoolsConnections.set(message.tabId, port);
|
|
702
|
+
console.log('[GxP DevTools] Panel connected for tab:', message.tabId);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Forward messages to content script
|
|
706
|
+
if (message.tabId && message.action) {
|
|
707
|
+
browser.tabs.sendMessage(message.tabId, message).then((response) => {
|
|
708
|
+
port.postMessage(response);
|
|
709
|
+
}).catch(console.error);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
port.onMessage.addListener(extensionListener);
|
|
714
|
+
|
|
715
|
+
port.onDisconnect.addListener(() => {
|
|
716
|
+
// Remove the connection when panel is closed
|
|
717
|
+
for (const [tabId, connectedPort] of devtoolsConnections.entries()) {
|
|
718
|
+
if (connectedPort === port) {
|
|
719
|
+
devtoolsConnections.delete(tabId);
|
|
720
|
+
console.log('[GxP DevTools] Panel disconnected for tab:', tabId);
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// Forward messages from content script to DevTools panel
|
|
729
|
+
function forwardToDevTools(tabId, message) {
|
|
730
|
+
const port = devtoolsConnections.get(tabId);
|
|
731
|
+
if (port) {
|
|
732
|
+
try {
|
|
733
|
+
port.postMessage(message);
|
|
734
|
+
} catch (error) {
|
|
735
|
+
console.error('[GxP DevTools] Error forwarding to panel:', error);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Handle messages from popup
|
|
741
|
+
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
742
|
+
console.log("[JavaScript Proxy] Received message:", request);
|
|
743
|
+
|
|
744
|
+
// Handle messages from content script about element selection
|
|
745
|
+
if (request.type === 'elementSelected' && sender.tab) {
|
|
746
|
+
forwardToDevTools(sender.tab.id, request);
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
switch (request.action) {
|
|
751
|
+
case "toggleProxy":
|
|
752
|
+
config.enabled = request.enabled;
|
|
753
|
+
browser.storage.sync.set({ enabled: config.enabled });
|
|
754
|
+
updateIcon();
|
|
755
|
+
updateRequestListener();
|
|
756
|
+
// Clear cache when enabling the proxy
|
|
757
|
+
if (config.enabled && config.clearCacheOnEnable) {
|
|
758
|
+
clearCacheForPattern(); // Clear cache for all enabled patterns
|
|
759
|
+
}
|
|
760
|
+
sendResponse({ success: true, enabled: config.enabled });
|
|
761
|
+
break;
|
|
762
|
+
|
|
763
|
+
case "updateConfig":
|
|
764
|
+
config = { ...config, ...request.config };
|
|
765
|
+
browser.storage.sync.set(config);
|
|
766
|
+
updateIcon();
|
|
767
|
+
updateRequestListener();
|
|
768
|
+
sendResponse({ success: true });
|
|
769
|
+
break;
|
|
770
|
+
|
|
771
|
+
case "getConfig":
|
|
772
|
+
sendResponse(config);
|
|
773
|
+
break;
|
|
774
|
+
|
|
775
|
+
case "clearCache":
|
|
776
|
+
clearCacheForPattern() // Clear cache for all enabled patterns
|
|
777
|
+
.then(() => {
|
|
778
|
+
sendResponse({ success: true });
|
|
779
|
+
})
|
|
780
|
+
.catch((error) => {
|
|
781
|
+
sendResponse({ success: false, error: error.message });
|
|
782
|
+
});
|
|
783
|
+
break;
|
|
784
|
+
|
|
785
|
+
default:
|
|
786
|
+
console.warn("[JavaScript Proxy] Unknown action:", request.action);
|
|
787
|
+
sendResponse({ success: false, error: "Unknown action" });
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
return true; // Keep message channel open for async response
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Clear expired proxy requests periodically
|
|
794
|
+
setInterval(() => {
|
|
795
|
+
const now = Date.now();
|
|
796
|
+
for (const [requestId, info] of pendingProxyRequests.entries()) {
|
|
797
|
+
if (now - info.timestamp > 30000) {
|
|
798
|
+
pendingProxyRequests.delete(requestId);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}, 10000);
|
|
802
|
+
|
|
803
|
+
// Initialize on startup
|
|
804
|
+
initializeExtension();
|