Просмотр исходного кода

truncate messages if too long

Former-commit-id: 081d40245ce7a5b5042bfbc2a15f11518dafa096
Gildas 5 лет назад
Родитель
Сommit
c5ecec3952

+ 73 - 16
extension/lib/single-file/fetch/bg/fetch.js

@@ -25,21 +25,74 @@
 
 (() => {
 
+	const MAX_CONTENT_SIZE = 8 * (1024 * 1024);
+
 	browser.runtime.onMessage.addListener((message, sender) => {
-		if (message.method && message.method.startsWith("singlefile.fetch")) {
-			return new Promise(resolve => {
-				onRequest(message, sender)
-					.then(resolve)
-					.catch(error => resolve({ error: error.toString() }));
-			});
+		if (message.method == "singlefile.fetchRequest"
+			|| message.method == "singlefile.fetchFrameRequest"
+			|| message.method == "singlefile.bgFetchFrameResponse") {
+			return onRequest(message, sender);
 		}
 	});
 
-	function onRequest(message, sender) {
-		if (message.method == "singlefile.fetch") {
-			return fetchResource(message.url);
-		} else if (message.method == "singlefile.fetchFrame") {
-			return browser.tabs.sendMessage(sender.tab.id, message);
+	async function onRequest(message, sender) {
+		if (message.method == "singlefile.fetchRequest") {
+			sendFetchResponse(message, sender);
+			return {};
+		} else if (message.method == "singlefile.fetchFrameRequest") {
+			return sendFrameMessage(message, sender);
+		} else if (message.method == "singlefile.bgFetchFrameResponse") {
+			sendFetchFrameResponse(message);
+			return {};
+		}
+	}
+
+	async function sendFrameMessage(message, sender) {
+		message.tabId = sender.tab.id;
+		try {
+			return await browser.tabs.sendMessage(sender.tab.id, message);
+		} catch (error) {
+			return null;
+		}
+	}
+
+	async function sendFetchResponse(message, sender) {
+		try {
+			const response = await fetchResource(message.url);
+			const id = message.id;
+			for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE < response.array.length; blockIndex++) {
+				const message = {
+					method: "singlefile.fetchResponse",
+					id
+				};
+				message.truncated = response.array.length > MAX_CONTENT_SIZE;
+				if (message.truncated) {
+					message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > response.array.length;
+					message.array = response.array.slice(blockIndex * MAX_CONTENT_SIZE, (blockIndex + 1) * MAX_CONTENT_SIZE);
+				} else {
+					message.array = response.array;
+				}
+				if (!message.truncated || message.finished) {
+					message.headers = response.headers;
+					message.status = response.status;
+				}
+				browser.tabs.sendMessage(sender.tab.id, message);
+			}
+		} catch (error) {
+			await browser.tabs.sendMessage(sender.tab.id, {
+				method: "singlefile.fetchResponse",
+				id: message.id,
+				error: error.toString()
+			});
+		}
+	}
+
+	function sendFetchFrameResponse(message) {
+		if (message.error) {
+			browser.tabs.sendMessage(message.tabId, message);
+		} else {
+			message.method = "singlefile.fetchFrameResponse";
+			browser.tabs.sendMessage(message.tabId, message);
 		}
 	}
 
@@ -51,11 +104,15 @@
 			xhrRequest.onerror = event => reject(new Error(event.detail));
 			xhrRequest.onreadystatechange = () => {
 				if (xhrRequest.readyState == XMLHttpRequest.DONE) {
-					resolve({
-						array: Array.from(new Uint8Array(xhrRequest.response)),
-						headers: { "content-type": xhrRequest.getResponseHeader("Content-Type") },
-						status: xhrRequest.status
-					});
+					if (xhrRequest.status || xhrRequest.response.byteLength) {
+						resolve({
+							array: Array.from(new Uint8Array(xhrRequest.response)),
+							headers: { "content-type": xhrRequest.getResponseHeader("Content-Type") },
+							status: xhrRequest.status
+						});
+					} else {
+						reject();
+					}
 				}
 			};
 			xhrRequest.open("GET", url, true);

+ 98 - 47
extension/lib/single-file/fetch/content/content-fetch.js

@@ -25,33 +25,19 @@
 
 this.singlefile.extension.lib.fetch.content.resources = this.singlefile.extension.lib.fetch.content.resources || (() => {
 
+	const MAX_CONTENT_SIZE = 8 * (1024 * 1024);
 	const FETCH_REQUEST_EVENT = "single-file-request-fetch";
 	const FETCH_RESPONSE_EVENT = "single-file-response-fetch";
 
+	const pendingRequests = [];
+
 	browser.runtime.onMessage.addListener(message => {
-		if (message.method == "singlefile.fetchFrame" && window.frameId && window.frameId == message.frameId) {
+		if ((message.method == "singlefile.fetchFrameRequest" && window.frameId && window.frameId == message.frameId)
+			|| message.method == "singlefile.fetchResponse"
+			|| message.method == "singlefile.fetchFrameResponse") {
 			return onMessage(message);
 		}
 	});
-
-	async function onMessage(message) {
-		try {
-			let response = await fetch(message.url, { cache: "force-cache" });
-			if (response.status == 403) {
-				response = hostFetch(message.url);
-			}
-			return {
-				status: response.status,
-				headers: [...response.headers],
-				array: Array.from(new Uint8Array(await response.arrayBuffer()))
-			};
-		} catch (error) {
-			return {
-				error: error.toString()
-			};
-		}
-	}
-
 	return {
 		fetch: async url => {
 			try {
@@ -62,40 +48,105 @@ this.singlefile.extension.lib.fetch.content.resources = this.singlefile.extensio
 				return response;
 			}
 			catch (error) {
-				const response = await sendMessage({ method: "singlefile.fetch", url });
-				return {
-					status: response.status,
-					headers: { get: headerName => response.headers[headerName] },
-					arrayBuffer: async () => new Uint8Array(response.array).buffer
-				};
+				return sendRequest("singlefile.fetchRequest", url);
 			}
 		},
-		frameFetch: async (url, frameId) => {
-			const response = await sendMessage({ method: "singlefile.fetchFrame", url, frameId });
-			return {
-				status: response.status,
-				headers: {
-					get: headerName => {
-						const headerArray = response.headers.find(headerArray => headerArray[0] == headerName);
-						if (headerArray) {
-							return headerArray[1];
-						}
-					}
-				},
-				arrayBuffer: async () => new Uint8Array(response.array).buffer
-			};
-		}
+		frameFetch: (url, frameId) => sendRequest("singlefile.fetchFrameRequest", url, frameId)
 	};
 
-	async function sendMessage(message) {
-		const response = await browser.runtime.sendMessage(message);
-		if (!response || response.error) {
-			throw new Error(response && response.error.toString());
-		} else {
-			return response;
+	async function onMessage(message) {
+		if (message.method == "singlefile.fetchFrameRequest") {
+			sendFetchFrameResponse(message);
+			return {};
+		} else if (message.method == "singlefile.fetchResponse" || message.method == "singlefile.fetchFrameResponse") {
+			resolveRequest(message);
+			return {};
+		}
+	}
+
+	async function sendFetchFrameResponse(message) {
+		const { id, tabId } = message;
+		try {
+			let response = await fetch(message.url, { cache: "force-cache" });
+			if (response.status == 403 || response.status == 404) {
+				response = hostFetch(message.url);
+			}
+			const array = new Uint8Array(await response.arrayBuffer());
+			for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE < array.length; blockIndex++) {
+				const message = {
+					method: "singlefile.bgFetchFrameResponse",
+					id,
+					tabId
+				};
+				message.truncated = array.length > MAX_CONTENT_SIZE;
+				if (message.truncated) {
+					message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > array.length;
+					message.array = array.slice(blockIndex * MAX_CONTENT_SIZE, (blockIndex + 1) * MAX_CONTENT_SIZE);
+				} else {
+					message.array = array;
+				}
+				message.array = Array.from(message.array);
+				const headers = {};
+				response.headers.forEach((value, key) => headers[key] = value);
+				if (!message.truncated || message.finished) {
+					message.headers = headers;
+					message.status = response.status;
+				}
+				browser.runtime.sendMessage(message);
+			}
+		} catch (error) {
+			await browser.runtime.sendMessage({
+				method: "singlefile.bgFetchFrameResponse",
+				id: message.id,
+				tabId,
+				error: error.toString()
+			});
+		}
+	}
+
+	function resolveRequest(message) {
+		const pendingRequest = pendingRequests[message.id];
+		if (pendingRequest) {
+			if (message.error) {
+				pendingRequest.reject(new Error(message.error.toString()));
+				pendingRequests[message.id] = null;
+			} else {
+				if (message.truncated) {
+					if (!pendingRequest.responseArray) {
+						pendingRequest.responseArray = [];
+					}
+					pendingRequest.responseArray = pendingRequest.responseArray.concat(message.array);
+				} else {
+					pendingRequest.responseArray = message.array;
+				}
+				if (!message.truncated || message.finished) {
+					pendingRequest.resolve({
+						status: message.status,
+						headers: {
+							get: headerName => message.headers[headerName]
+						},
+						arrayBuffer: async () => new Uint8Array(pendingRequest.responseArray).buffer
+					});
+					pendingRequests[message.id] = null;
+				}
+			}
 		}
 	}
 
+	function sendRequest(method, url, frameId) {
+		return new Promise((resolve, reject) => {
+			const responsePromise = browser.runtime.sendMessage({ method, url, frameId, id: pendingRequests.length });
+			responsePromise
+				.then(response => {
+					if (!response) {
+						reject();
+					}
+				})
+				.catch(reject);
+			pendingRequests.push({ resolve, reject });
+		});
+	}
+
 	function hostFetch(url) {
 		return new Promise((resolve, reject) => {
 			dispatchEvent(new CustomEvent(FETCH_REQUEST_EVENT, { detail: url }));