-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathbackground.js
More file actions
191 lines (178 loc) · 6.43 KB
/
background.js
File metadata and controls
191 lines (178 loc) · 6.43 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/**
* Possible parameters for request:
* action: "xhttp" for a cross-origin HTTP request
* method: Default "GET"
* url : required, but not validated
* data : data to send in a POST request
* variable : pass private variable into the callback
*
* The callback function is called upon completion of the request
* https://stackoverflow.com/questions/7699615/cross-domain-xmlhttprequest-using-background-pages
*
* Call to verify HistoryLink authentication to Geni & query Family Data
* */
function getUrlFromJson(obj) {
let result = "";
for (var key in obj) {
if (result != "") {
result += "&";
}
result += key + "=" + encodeURIComponent(obj[key]);
}
return result;
}
function getJsonFromUrl(query) {
if (typeof query === 'string') {
var result = {};
query.split("&").forEach(function(part) {
var item = part.split("=");
result[item[0]] = decodeURIComponent(item[1]);
});
return result;
} else {
return query;
}
}
// listen for messages - if they include photo URLs, intercept and get them
chrome.runtime.onMessage.addListener( function(request, sender, callback) {
if (request.action == "xhttp") {
if (request.latency){
delay(request.latency);// see https://www.nginx.com/blog/rate-limiting-nginx/
}
const method = request.method ? request.method.toUpperCase() : 'GET';
if (method == 'POST') {
(async () => {
try {
let jsData = getJsonFromUrl(request.data);
if (jsData.photo !== undefined) {
const photoResponse = await fetch(jsData.photo);
if (!photoResponse.ok) {
callback({error: photoResponse.error, responseURL: photoResponse.responseURL});
return;
}
const buf = await photoResponse.arrayBuffer();
let binary = "";
const bytes = new Uint8Array(buf);
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i] & 0xff);
}
delete jsData.photo;
jsData.file = btoa(binary);
}
const body = getUrlFromJson(jsData);
const response = await fetch(request.url, {
method: 'POST',
body: body,
headers: {"Content-Type": "application/x-www-form-urlencoded"}
});
const responseText = await response.text();
if (!response.ok) {
console.error("Unable to get XMLHttpRequest: " + request.url);
callback({error: response.error, variable: request.variable, responseURL: response.responseURL});
} else {
callback({source: responseText, variable: request.variable, responseURL: response.url});
}
} catch (error) {
console.error("Fetch POST failed: ", error);
callback({error: error.message, variable: request.variable});
}
})();
} else {
// pass this request through - note that all receivers need to change for fetch response
var vartn = {variable: request.variable};
fetch(request.url,
{
method: "GET",
body: request.data
}
).then((response) => {
vartn.responseURL = response.url;
return response.text();
}).then((source) => {
vartn.source = source;
}).catch((error) => {
console.error("Fetch GET failed: ", error)
}).finally(() => {
callback(vartn);
});
}
return true; // prevents the callback from being called too early on return
} else if (request.action == "icon") {
chrome.action.setIcon({path: request.path});
return true;
} else if (request.action == "eval") {
evalObject(request.variable, callback);
return true;
}
return false;
});
async function evalObject(expression, callback) {
await setupOffscreenDocument("offscreen.html");
await chrome.runtime.sendMessage({
action: "offscreen",
target: "offscreen",
data: expression
}, function(response) {
callback(response);
});
}
function exists(object) {
return (typeof object !== "undefined" && object !== null);
}
const iframeHosts = ['www.geni.com',];
chrome.runtime.onInstalled.addListener(() => {
const RULE = {
id: 1,
condition: {
initiatorDomains: [chrome.runtime.id],
requestDomains: iframeHosts,
resourceTypes: ['main_frame', 'sub_frame'],
},
action: {
type: 'modifyHeaders',
responseHeaders: [
{header: 'X-Frame-Options', operation: 'remove'},
{header: 'Frame-Options', operation: 'remove'},
// Uncomment the following line to suppress `frame-ancestors` error
// {header: 'Content-Security-Policy', operation: 'remove'},
],
},
};
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [RULE.id],
addRules: [RULE],
});
console.log("Header rule installed");
});
let creating; // A global promise to avoid concurrency issues
async function setupOffscreenDocument(path) {
// Check all windows controlled by the service worker to see if one
// of them is the offscreen document with the given path
const offscreenUrl = chrome.runtime.getURL(path);
const existingContexts = await chrome.runtime.getContexts({
contextTypes: ['OFFSCREEN_DOCUMENT'],
documentUrls: [offscreenUrl]
});
if (existingContexts.length > 0) {
return;
}
// create offscreen document
if (creating) {
await creating;
} else {
creating = chrome.offscreen.createDocument({
url: path,
reasons: ['CLIPBOARD'],
justification: 'reason for needing the document',
});
await creating;
creating = null;
}
}
function delay(ms){
let currDate = new Date().getTime();
let dateNow = currDate;
while(dateNow<currDate+ms){
dateNow = new Date().getTime();
}
}